日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Java+MySQL实现网络爬虫程序

發布時間:2023/12/18 数据库 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java+MySQL实现网络爬虫程序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
???網絡爬蟲,也叫網絡蜘蛛,有的項目也把它稱作“walker”。維基百科所給的定義是“一種系統地掃描互聯網,以獲取索引為目的的網絡程序”。網絡上有很多關于網絡爬蟲的開源項目,其中比較有名的是Heritrix和Apache Nutch。有時需要在網上搜集信息,如果需要搜集的是獲取方法單一而人工搜集費時費力的信息,比如統計一個網站每個月發了多少篇文章、用了哪些標簽,為自然語言處理項目搜集語料,或者為模式識別項目搜集圖片等等,就需要爬蟲程序來完成這樣的任務。而且搜索引擎必不可少的組件之一也是網絡爬蟲。很多網絡爬蟲都是用Python,Java或C#實現的。我這里給出的是Java版本的爬蟲程序。為了節省時間和空間,我把程序限制在只掃描本博客地址下的網頁(也就是http://johnhan.net/但不包括http://johnhany.net/wp-content/下的內容),并從網址中統計出所用的所有標簽。只要稍作修改,去掉代碼里的限制條件就能作為掃描整個網絡的程序使用。或者對輸出格式稍作修改,可以作為生成博客sitemap的工具。代碼也可以在這里下載:johnhany/WPCrawler。 環境需求 ? ? ? ? 我的開發環境是Windows7 +?Eclipse。需要XAMPP提供通過url訪問MySQL數據庫的端口。還要用到三個開源的Java類庫:Apache HttpComponents 4.3?提供HTTP接口,用來向目標網址提交HTTP請求,以獲取網頁的內容;HTML Parser 2.0?用來解析網頁,從DOM節點中提取網址鏈接;MySQL Connector/J 5.1.27?連接Java程序和MySQL,然后就可以用Java代碼操作數據庫。 代碼 ? ? ? ? 代碼位于三個文件中,分別是:crawler.java,httpGet.java和parsePage.java。包名為net.johnhany.wpcrawler。 crawler.java

package net.johnhany.wpcrawler; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class crawler {public static void main(String args[]) throws Exception {String frontpage = "http://johnhany.net/";Connection conn = null;//connect the MySQL databasetry {Class.forName("com.mysql.jdbc.Driver");String dburl = "jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf8";conn = DriverManager.getConnection(dburl, "root", "");System.out.println("connection built");} catch (SQLException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}String sql = null;String url = frontpage;Statement stmt = null;ResultSet rs = null;int count = 0;if(conn != null) {//create database and table that will be neededtry {sql = "CREATE DATABASE IF NOT EXISTS crawler";stmt = conn.createStatement();stmt.executeUpdate(sql);sql = "USE crawler";stmt = conn.createStatement();stmt.executeUpdate(sql);sql = "create table if not exists record (recordID int(5) not null auto_increment, URL text not null, crawled tinyint(1) not null, primary key (recordID)) engine=InnoDB DEFAULT CHARSET=utf8";stmt = conn.createStatement();stmt.executeUpdate(sql);sql = "create table if not exists tags (tagnum int(4) not null auto_increment, tagname text not null, primary key (tagnum)) engine=InnoDB DEFAULT CHARSET=utf8";stmt = conn.createStatement();stmt.executeUpdate(sql);} catch (SQLException e) {e.printStackTrace();}//crawl every link in the databasewhile(true) {//get page content of link "url"httpGet.getByString(url,conn);count++;//set boolean value "crawled" to true after crawling this pagesql = "UPDATE record SET crawled = 1 WHERE URL = '" + url + "'";stmt = conn.createStatement();if(stmt.executeUpdate(sql) > 0) {//get the next page that has not been crawled yetsql = "SELECT * FROM record WHERE crawled = 0";stmt = conn.createStatement();rs = stmt.executeQuery(sql);if(rs.next()) {url = rs.getString(2);}else {//stop crawling if reach the bottom of the listbreak;}//set a limit of crawling countif(count > 1000 || url == null) {break;}}}conn.close();conn = null;System.out.println("Done.");System.out.println(count);}} } packagenet.johnhany.wpcrawler; importjava.io.IOException; importjava.sql.Connection; importorg.apache.http.HttpEntity; importorg.apache.http.HttpResponse; importorg.apache.http.client.ClientProtocolException; importorg.apache.http.client.ResponseHandler; importorg.apache.http.client.methods.HttpGet; importorg.apache.http.impl.client.CloseableHttpClient; importorg.apache.http.impl.client.HttpClients; importorg.apache.http.util.EntityUtils; publicclasshttpGet{ publicfinalstaticvoidgetByString(Stringurl,Connectionconn)throwsException{CloseableHttpClienthttpclient=HttpClients.createDefault(); try{HttpGethttpget=newHttpGet(url);System.out.println("executing request "+httpget.getURI()); ResponseHandler<String>responseHandler=newResponseHandler<String>(){ publicStringhandleResponse(finalHttpResponseresponse)throwsClientProtocolException,IOException{intstatus=response.getStatusLine().getStatusCode();if(status>=200&&status<300){HttpEntityentity=response.getEntity();returnentity!=null?EntityUtils.toString(entity):null;}else{thrownewClientProtocolException("Unexpected response status: "+status);}}};StringresponseBody=httpclient.execute(httpget,responseHandler);/*//print the content of the pageSystem.out.println("----------------------------------------");System.out.println(responseBody);System.out.println("----------------------------------------");*/parsePage.parseFromString(responseBody,conn); }finally{httpclient.close();}} } package net.johnhany.wpcrawler; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.htmlparser.Node; import org.htmlparser.Parser; import org.htmlparser.filters.HasAttributeFilter; import org.htmlparser.tags.LinkTag; import org.htmlparser.util.NodeList; import org.htmlparser.util.ParserException; import java.net.URLDecoder; public class parsePage { public static void parseFromString(String content, Connection conn) throws Exception {Parser parser = new Parser(content);HasAttributeFilter filter = new HasAttributeFilter("href"); try {NodeList list = parser.parse(filter);int count = list.size(); //process every link on this pagefor(int i=0; i<count; i++) {Node node = list.elementAt(i); if(node instanceof LinkTag) {LinkTag link = (LinkTag) node;String nextlink = link.extractLink();String mainurl = "http://johnhany.net/";String wpurl = mainurl + "wp-content/"; //only save page from "http://johnhany.net"if(nextlink.startsWith(mainurl)) {String sql = null;ResultSet rs = null;PreparedStatement pstmt = null;Statement stmt = null;String tag = null; //do not save any page from "wp-content"if(nextlink.startsWith(wpurl)) {continue;} try {//check if the link already exists in the databasesql = "SELECT * FROM record WHERE URL = '" + nextlink + "'";stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);rs = stmt.executeQuery(sql); if(rs.next()) { }else {//if the link does not exist in the database, insert itsql = "INSERT INTO record (URL, crawled) VALUES ('" + nextlink + "',0)";pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);pstmt.execute();System.out.println(nextlink); //use substring for better comparison performancenextlink = nextlink.substring(mainurl.length());//System.out.println(nextlink); if(nextlink.startsWith("tag/")) {tag = nextlink.substring(4, nextlink.length()-1);//decode in UTF-8 for Chinese characterstag = URLDecoder.decode(tag,"UTF-8");sql = "INSERT INTO tags (tagname) VALUES ('" + tag + "')";pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);//if the links are different from each other, the tags must be different//so there is no need to check if the tag already existspstmt.execute();}}} catch (SQLException e) {//handle the exceptionsSystem.out.println("SQLException: " + e.getMessage());System.out.println("SQLState: " + e.getSQLState());System.out.println("VendorError: " + e.getErrorCode());} finally {//close and release the resources of PreparedStatement, ResultSet and Statementif(pstmt != null) {try {pstmt.close();} catch (SQLException e2) {}}pstmt = null; if(rs != null) {try {rs.close();} catch (SQLException e1) {}}rs = null; if(stmt != null) {try {stmt.close();} catch (SQLException e3) {}}stmt = null;} }}}} catch (ParserException e) {e.printStackTrace();}} } 程序原理

httpGet.java

parsePage.java

? 所謂“互聯網”,是網狀結構,任意兩個節點間都有可能存在路徑。爬蟲程序對互聯網的掃描,在圖論角度來講,就是對有向圖的遍歷(鏈接是從一個網頁指向另一個網頁,所以是有向的)。常見的遍歷方法有深度優先和廣度優先兩種。相關理論知識可以參考樹的遍歷:這里和這里。我的程序采用的是廣度優先方式。

? ???程序從crawler.java的main()開始運行。

Class.forName("com.mysql.jdbc.Driver"); Stringdburl="jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf8"; conn=DriverManager.getConnection(dburl,"root",""); System.out.println("connection built");
首先,調用DriverManager連接MySQL服務。這里使用的是XAMPP的默認MySQL端口3306,端口值可以在XAMPP主界面看到:

? ? ? ? Apache和MySQL都啟動之后,在瀏覽器地址欄輸入“http://localhost/phpmyadmin/”就可以看到數據庫了。等程序運行完之后可以在這里檢查一下運行是否正確。

?

sql = "CREATE DATABASE IF NOT EXISTS crawler"; stmt = conn.createStatement(); stmt.executeUpdate(sql);sql = "USE crawler"; stmt = conn.createStatement(); stmt.executeUpdate(sql);sql = "create table if not exists record (recordID int(5) not null auto_increment, URL text not null, crawled tinyint(1) not null, primary key (recordID)) engine=InnoDB DEFAULT CHARSET=utf8"; stmt = conn.createStatement(); stmt.executeUpdate(sql);sql = "create table if not exists tags (tagnum int(4) not null auto_increment, tagname text not null, primary key (tagnum)) engine=InnoDB DEFAULT CHARSET=utf8"; stmt = conn.createStatement(); stmt.executeUpdate(sql);

? ? ? ? 連接好數據庫后,建立一個名為“crawler”的數據庫,在庫里建兩個表,一個叫“record”,包含字段“recordID”,“URL”和“crawled”,分別記錄地址編號、鏈接地址和地址是否被掃描過;另一個叫“tags”,包含字段“tagnum”和“tagname”,分別記錄標簽編號和標簽名。

while(true){ httpGet.getByString(url,conn); count++; sql="UPDATE record SET crawled = 1 WHERE URL = '"+url+"'"; stmt=conn.createStatement(); if(stmt.executeUpdate(sql)>0){ sql="SELECT * FROM record WHERE crawled = 0"; stmt=conn.createStatement(); rs=stmt.executeQuery(sql); if(rs.next()){ url=rs.getString(2); }else{ break; } } }


? ? ?

接著在一個while循環內依次處理表record內的每個地址。每次處理時,把地址url傳遞給httpGet.getByString(),然后在表record中把crawled改為true,表明這個地址已經處理過。然后尋找下一個crawled為false的地址,繼續處理,直到處理到表尾。

?這里需要注意的細節是,執行executeQuery()后,得到了一個ResultSet結構rs,rs包含SQL查詢返回的所有行和一個指針,指針指向結果中第一行之前的位置,需要執行一次rs.next()才能讓rs的指針指向第一個結果,同時返回true,之后每次執行rs.next()都會把指針移到下一個結果上并返回true,直至再也沒有結果時,rs.next()的返回值變成了false。

? ? ? ? 還有一個細節,在執行建庫建表、INSERT、UPDATE時,需要用executeUpdate();在執行SELECT時,需要使用executeQuery()。executeQuery()總是返回一個ResultSet,executeUpdate()返回符合查詢的行數。

? ? ? ??httpGet.java的getByString()類負責向所給的網址發送請求,然后下載網頁內容。

HttpGet httpget = new HttpGet(url); System.out.println("executing request " + httpget.getURI());ResponseHandler<String> responseHandler = new ResponseHandler<String>() {public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {int status = response.getStatusLine().getStatusCode();if (status >= 200 && status < 300) {HttpEntity entity = response.getEntity();return entity != null ? EntityUtils.toString(entity) : null;} else {throw new ClientProtocolException("Unexpected response status: " + status);}} }; String responseBody = httpclient.execute(httpget, responseHandler);

? ? ? ? 這段代碼是HTTPComponents的HTTP Client組件中給出的樣例,在很多情況下可以直接使用。這部分代碼獲得了一個字符串responseBody,里面保存著網頁中的全部字符。

? ? ? ? 接著,就需要把responseBody傳遞給parsePage.java的parseFromString類提取鏈接

Parserparser=newParser(content); HasAttributeFilterfilter=newHasAttributeFilter("href");try{ NodeListlist=parser.parse(filter); intcount=list.size(); //process every link on this page for(inti=0;i<count;i++){ Nodenode=list.elementAt(i); if(nodeinstanceofLinkTag){

? 在HTML文件中,鏈接一般都在a標簽的href屬性中,所以需要創建一個屬性過濾器。NodeList保存著這個HTML文件中的所有DOM節點,通過在for循環中依次處理每個節點尋找符合要求的標簽,可以把網頁中的所有鏈接提取出來。

? ? ? ? 然后通過nextlink.startsWith()進一步篩選,只處理以“http://johnhany.net/”開頭的鏈接并跳過以“http://johnhany.net/wp-content/”開頭的鏈接。

sql="SELECT * FROM record WHERE URL = '"+nextlink+"'"; stmt=conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE); rs=stmt.executeQuery(sql);if(rs.next()){ }else{ //if the link does not exist in the database, insert it sql="INSERT INTO record (URL, crawled) VALUES ('"+nextlink+"',0)"; pstmt=conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); pstmt.execute();
? ? ?在表record中查找是否已經存在這個鏈接,如果存在(rs.next()==true),不做任何處理;如果不存在(rs.next()==false),在表中插入這個地址并把crawled置為false。因為之前recordID設為AUTO_INCREMENT,所以要用 Statement.RETURN_GENERATED_KEYS獲取適當的編號

nextlink=nextlink.substring(mainurl.length());if(nextlink.startsWith("tag/")){ tag=nextlink.substring(4,nextlink.length()-1); tag=URLDecoder.decode(tag,"UTF-8"); sql="INSERT INTO tags (tagname) VALUES ('"+tag+"')"; pstmt=conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); pstmt.execute();

?? 去掉鏈接開頭的“http://johnhany.net/”幾個字符,提高字符比較的速度。如果含有“tag/”說明其后的字符是一個標簽的名字,把這給名字提取出來,用UTF-8編碼,保證漢字的正常顯示,然后存入表tags。類似地還可以加入判斷“article/”,“author/”,或“2013/11/”等對其他鏈接進行歸類。

結果

這是兩張數據庫的截圖,顯示了程序的部分結果:

? ? ? ? 在這里可以獲得全部輸出結果。可以與本博客的sitemap比較一下,看看如果想在其基礎上實現sitemap生成工具,還要做哪些修改。

附錄:網摘:爬蟲的簡單實現

網絡爬蟲是一個自動提取網頁的程序,它為搜索引擎從萬維網上下載網頁,是搜索引擎的重要組成,其基本架構如下圖所示:

傳統爬蟲從一個或若干初始網頁的URL開始,獲得初始網頁上的URL,在抓取網頁的過程中,不斷從當前頁面上抽取新的URL放入隊列,直到滿足系統的一定停止條件。對于垂直搜索來說,聚焦爬蟲,即有針對性地爬取特定主題網頁的爬蟲,更為適合。

本文爬蟲程序的核心代碼如下:

Java代碼

public void crawl() throws Throwable { while (continueCrawling()) { CrawlerUrl url = getNextUrl(); //獲取待爬取隊列中的下一個URL if (url != null) { printCrawlInfo(); String content = getContent(url); //獲取URL的文本信息 //聚焦爬蟲只爬取與主題內容相關的網頁,這里采用正則匹配簡單處理 if (isContentRelevant(content, this.regexpSearchPattern)) { saveContent(url, content); //保存網頁至本地 //獲取網頁內容中的鏈接,并放入待爬取隊列中 Collection urlStrings = extractUrls(content, url); addUrlsToUrlQueue(url, urlStrings); } else { System.out.println(url + " is not relevant ignoring ..."); } //延時防止被對方屏蔽 Thread.sleep(this.delayBetweenUrls); } } closeOutputStream(); }

整個函數由getNextUrl、getContent、isContentRelevant、extractUrls、addUrlsToUrlQueue等幾個核心方法組成,下面將一一介紹。先看getNextUrl:

Java代碼

private CrawlerUrl getNextUrl() throws Throwable { CrawlerUrl nextUrl = null; while ((nextUrl == null) && (!urlQueue.isEmpty())) { CrawlerUrl crawlerUrl = this.urlQueue.remove(); //doWeHavePermissionToVisit:是否有權限訪問該URL,友好的爬蟲會根據網站提供的"Robot.txt"中配置的規則進行爬取 //isUrlAlreadyVisited:URL是否訪問過,大型的搜索引擎往往采用BloomFilter進行排重,這里簡單使用HashMap //isDepthAcceptable:是否達到指定的深度上限。爬蟲一般采取廣度優先的方式。一些網站會構建爬蟲陷阱(自動生成一些無效鏈接使爬蟲陷入死循環),采用深度限制加以避免 if (doWeHavePermissionToVisit(crawlerUrl) && (!isUrlAlreadyVisited(crawlerUrl)) && isDepthAcceptable(crawlerUrl)) { nextUrl = crawlerUrl; // System.out.println("Next url to be visited is " + nextUrl); } } return nextUrl; }

更多的關于robot.txt的具體寫法,可參考以下這篇文章:

http://www.bloghuman.com/post/67/

getContent內部使用apache的httpclient 4.1獲取網頁內容,具體代碼如下:

Java代碼

private String getContent(CrawlerUrl url) throws Throwable { //HttpClient4.1的調用與之前的方式不同 HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url.getUrlString()); StringBuffer strBuf = new StringBuffer(); HttpResponse response = client.execute(httpGet); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) { HttpEntity entity = response.getEntity(); if (entity != null) { BufferedReader reader = new BufferedReader( new InputStreamReader(entity.getContent(), "UTF-8")); String line = null; if (entity.getContentLength() > 0) { strBuf = new StringBuffer((int) entity.getContentLength()); while ((line = reader.readLine()) != null) { strBuf.append(line); } } } if (entity != null) { entity.consumeContent(); } } //將url標記為已訪問 markUrlAsVisited(url); return strBuf.toString(); }

對于垂直型應用來說,數據的準確性往往更為重要。聚焦型爬蟲的主要特點是,只收集和主題相關的數據,這就是isContentRelevant方法的作用。這里或許要使用分類預測技術,為簡單起見,采用正則匹配來代替。其主要代碼如下:

Java代碼

public static boolean isContentRelevant(String content, Pattern regexpPattern) { boolean retValue = false; if (content != null) { //是否符合正則表達式的條件 Matcher m = regexpPattern.matcher(content.toLowerCase()); retValue = m.find(); } return retValue; }

extractUrls的主要作用,是從網頁中獲取更多的URL,包括內部鏈接和外部鏈接,代碼如下:

Java代碼

public List extractUrls(String text, CrawlerUrl crawlerUrl) { Map urlMap = new HashMap(); extractHttpUrls(urlMap, text); extractRelativeUrls(urlMap, text, crawlerUrl); return new ArrayList(urlMap.keySet()); } //處理外部鏈接 private void extractHttpUrls(Map urlMap, String text) { Matcher m = httpRegexp.matcher(text); while (m.find()) { String url = m.group(); String[] terms = url.split("a href=\""); for (String term : terms) { // System.out.println("Term = " + term); if (term.startsWith("http")) { int index = term.indexOf("\""); if (index > 0) { term = term.substring(0, index); } urlMap.put(term, term); System.out.println("Hyperlink: " + term); } } } } //處理內部鏈接 private void extractRelativeUrls(Map urlMap, String text, CrawlerUrl crawlerUrl) { Matcher m = relativeRegexp.matcher(text); URL textURL = crawlerUrl.getURL(); String host = textURL.getHost(); while (m.find()) { String url = m.group(); String[] terms = url.split("a href=\""); for (String term : terms) { if (term.startsWith("/")) { int index = term.indexOf("\""); if (index > 0) { term = term.substring(0, index); } String s = "http://" + host + term; urlMap.put(s, s); System.out.println("Relative url: " + s); } } } }

如此,便構建了一個簡單的網絡爬蟲程序,可以使用以下程序來測試它:

Java代碼

public static void main(String[] args) { try { String url = "http://www.amazon.com"; Queue urlQueue = new LinkedList(); String regexp = "java"; urlQueue.add(new CrawlerUrl(url, 0)); NaiveCrawler crawler = new NaiveCrawler(urlQueue, 100, 5, 1000L, regexp); // boolean allowCrawl = crawler.areWeAllowedToVisit(url); // System.out.println("Allowed to crawl: " + url + " " + // allowCrawl); crawler.crawl(); } catch (Throwable t) { System.out.println(t.toString()); t.printStackTrace(); } }

當然,你可以為它賦予更為高級的功能,比如多線程、更智能的聚焦、結合Lucene建立索引等等。更為復雜的情況,可以考慮使用一些開源的蜘蛛程序,比如Nutch或是Heritrix等等,就不在本文的討論范圍了。


總結

以上是生活随笔為你收集整理的Java+MySQL实现网络爬虫程序的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。