最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Java HttpClient和Htmlparser實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲(chóng)代碼

 更新時(shí)間:2015年12月11日 10:23:29   作者:jerrylsxu  
這篇文章主要介紹了基于Java HttpClient和Htmlparser實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲(chóng)代碼的相關(guān)資料,需要的朋友可以參考下

開(kāi)發(fā)環(huán)境的搭建,在工程的 Build Path 中導(dǎo)入下載的Commons-httpClient3.1.Jar,htmllexer.jar 以及 htmlparser.jar 文件。

圖 1. 開(kāi)發(fā)環(huán)境搭建

HttpClient 基本類庫(kù)使用

HttpClinet 提供了幾個(gè)類來(lái)支持 HTTP 訪問(wèn)。下面我們通過(guò)一些示例代碼來(lái)熟悉和說(shuō)明這些類的功能和使用。 HttpClient 提供的 HTTP 的訪問(wèn)主要是通過(guò) GetMethod 類和 PostMethod 類來(lái)實(shí)現(xiàn)的,他們分別對(duì)應(yīng)了 HTTP Get 請(qǐng)求與 Http Post 請(qǐng)求。

GetMethod

使用 GetMethod 來(lái)訪問(wèn)一個(gè) URL 對(duì)應(yīng)的網(wǎng)頁(yè),需要如下一些步驟。
生成一個(gè) HttpClinet 對(duì)象并設(shè)置相應(yīng)的參數(shù)。
生成一個(gè) GetMethod 對(duì)象并設(shè)置響應(yīng)的參數(shù)。
用 HttpClinet 生成的對(duì)象來(lái)執(zhí)行 GetMethod 生成的 Get 方法。
處理響應(yīng)狀態(tài)碼。
若響應(yīng)正常,處理 HTTP 響應(yīng)內(nèi)容。
釋放連接。

清單 1 的代碼展示了這些步驟,其中的注釋對(duì)代碼進(jìn)行了較詳細(xì)的說(shuō)明。

清單 1.

/* 1 生成 HttpClinet 對(duì)象并設(shè)置參數(shù)*/
 HttpClient httpClient=new HttpClient();
 //設(shè)置 Http 連接超時(shí)為5秒
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
  
 /*2 生成 GetMethod 對(duì)象并設(shè)置參數(shù)*/
 GetMethod getMethod=new GetMethod(url);   
 //設(shè)置 get 請(qǐng)求超時(shí)為 5 秒
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT,5000);
 //設(shè)置請(qǐng)求重試處理,用的是默認(rèn)的重試處理:請(qǐng)求三次
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
     new DefaultHttpMethodRetryHandler());
  
 /*3 執(zhí)行 HTTP GET 請(qǐng)求*/
 try{ 
   int statusCode = httpClient.executeMethod(getMethod);
   /*4 判斷訪問(wèn)的狀態(tài)碼*/
   if (statusCode != HttpStatus.SC_OK) 
   {
System.err.println("Method failed: "+ getMethod.getStatusLine());
   }
  
   /*5 處理 HTTP 響應(yīng)內(nèi)容*/
   //HTTP響應(yīng)頭部信息,這里簡(jiǎn)單打印
 Header[] headers=getMethod.getResponseHeaders();
   for(Header h: headers)
      System.out.println(h.getName()+" "+h.getValue());*/
   //讀取 HTTP 響應(yīng)內(nèi)容,這里簡(jiǎn)單打印網(wǎng)頁(yè)內(nèi)容
   byte[] responseBody = getMethod.getResponseBody();//讀取為字節(jié)數(shù)組
System.out.println(new String(responseBody));
   //讀取為 InputStream,在網(wǎng)頁(yè)內(nèi)容數(shù)據(jù)量大時(shí)候推薦使用
   InputStream response = getMethod.getResponseBodyAsStream();//
   …
}
catch (HttpException e) 
{
   // 發(fā)生致命的異常,可能是協(xié)議不對(duì)或者返回的內(nèi)容有問(wèn)題
     System.out.println("Please check your provided http address!");
e.printStackTrace();
   } 
catch (IOException e)
 {
      // 發(fā)生網(wǎng)絡(luò)異常
    e.printStackTrace();
   } finally {
         /*6 .釋放連接*/
      getMethod.releaseConnection();      
      } 

這里值得注意的幾個(gè)地方是:

設(shè)置連接超時(shí)和請(qǐng)求超時(shí),這兩個(gè)超時(shí)的意義不同,需要分別設(shè)置。
響應(yīng)狀態(tài)碼的處理。

返回的結(jié)果可以為字節(jié)數(shù)組,也可以為 InputStream,而后者在網(wǎng)頁(yè)內(nèi)容數(shù)據(jù)量較大的時(shí)候推薦使用。
在處理返回結(jié)果的時(shí)候可以根據(jù)自己的需要,進(jìn)行相應(yīng)的處理。如筆者是需要保存網(wǎng)頁(yè)
到本地,因此就可以寫(xiě)一個(gè) saveToLocaleFile(byte[] data, String filePath) 的方法,將字節(jié)數(shù)組保存成本地文件。后續(xù)的簡(jiǎn)易爬蟲(chóng)部分會(huì)有相應(yīng)的介紹。

PostMethod

PostMethod 方法與 GetMethod 方法的使用步驟大體相同。但是由于 PostMethod 使用的是HTTP 的 Post 請(qǐng)求,因而請(qǐng)求參數(shù)的設(shè)置與 GetMethod 有所不同。在 GetMethod 中,請(qǐng)求的參數(shù)直接寫(xiě)在 URL 里,一般以這樣形式出現(xiàn):http://hostname:port//file?name1=value1&name2=value …。請(qǐng)求參數(shù)是 name,value 對(duì)。比如我想得到百度搜索“Thinking In Java”的結(jié)果網(wǎng)頁(yè),就可以使 GetMethod 的構(gòu)造方法中的 url 為:http://www.baidu.com/s?wd=Thinking+In+Java 。而 PostMethod 則可以模擬網(wǎng)頁(yè)里表單提交的過(guò)程,通過(guò)設(shè)置表單里 post 請(qǐng)求參數(shù)的值,來(lái)動(dòng)態(tài)的獲得返回的網(wǎng)頁(yè)結(jié)果。清單 2 中的代碼展示了如何創(chuàng)建一個(gè) Post 對(duì)象,并設(shè)置相應(yīng)的請(qǐng)求參數(shù)。

清單2

PostMethod postMethod = new PostMethod("http://dict.cn/");
postMethod.setRequestBody(new NameValuePair[]{new NameValuePair("q","java")}); 

HtmlParser 基本類庫(kù)使用

HtmlParser 提供了強(qiáng)大的類庫(kù)來(lái)處理 Internet 上的網(wǎng)頁(yè),可以實(shí)現(xiàn)對(duì)網(wǎng)頁(yè)特定內(nèi)容的提取和修改。下面通過(guò)幾個(gè)例子來(lái)介紹 HtmlParser 的一些使用。這些例子其中的代碼,有部分用在了后面介紹的簡(jiǎn)易爬蟲(chóng)中。以下所有的代碼和方法都在在類 HtmlParser.Test.java 里,這是筆者編寫(xiě)的一個(gè)用來(lái)測(cè)試 HtmlParser 用法的類。

迭代遍歷網(wǎng)頁(yè)所有節(jié)點(diǎn)

網(wǎng)頁(yè)是一個(gè)半結(jié)構(gòu)化的嵌套文本文件,有類似 XML 文件的樹(shù)形嵌套結(jié)構(gòu)。使用HtmlParser 可以讓我們輕易的迭代遍歷網(wǎng)頁(yè)的所有節(jié)點(diǎn)。清單 3 展示了如何來(lái)實(shí)現(xiàn)這個(gè)功能。

清單 3

// 循環(huán)訪問(wèn)所有節(jié)點(diǎn),輸出包含關(guān)鍵字的值節(jié)點(diǎn)
public static void extractKeyWordText(String url, String keyword) {
    try {
      //生成一個(gè)解析器對(duì)象,用網(wǎng)頁(yè)的 url 作為參數(shù)
      Parser parser = new Parser(url);
      //設(shè)置網(wǎng)頁(yè)的編碼,這里只是請(qǐng)求了一個(gè) gb2312 編碼網(wǎng)頁(yè)
      parser.setEncoding("gb2312");
      //迭代所有節(jié)點(diǎn), null 表示不使用 NodeFilter
      NodeList list = parser.parse(null);
      //從初始的節(jié)點(diǎn)列表跌倒所有的節(jié)點(diǎn)
      processNodeList(list, keyword);
    } catch (ParserException e) {
      e.printStackTrace();
    }
  }
 
  private static void processNodeList(NodeList list, String keyword) {
    //迭代開(kāi)始
    SimpleNodeIterator iterator = list.elements();
    while (iterator.hasMoreNodes()) {
      Node node = iterator.nextNode();
      //得到該節(jié)點(diǎn)的子節(jié)點(diǎn)列表
      NodeList childList = node.getChildren();
      //孩子節(jié)點(diǎn)為空,說(shuō)明是值節(jié)點(diǎn)
      if (null == childList)
      {
        //得到值節(jié)點(diǎn)的值
        String result = node.toPlainTextString();
        //若包含關(guān)鍵字,則簡(jiǎn)單打印出來(lái)文本
        if (result.indexOf(keyword) != -1)
          System.out.println(result);
      } //end if
      //孩子節(jié)點(diǎn)不為空,繼續(xù)迭代該孩子節(jié)點(diǎn)
      else
      {
        processNodeList(childList, keyword);
      }//end else
    }//end wile
  } 

上面的中有兩個(gè)方法:

private static void processNodeList(NodeList list, String keyword) 

該方法是用類似深度優(yōu)先的方法來(lái)迭代遍歷整個(gè)網(wǎng)頁(yè)節(jié)點(diǎn),將那些包含了某個(gè)關(guān)鍵字的值節(jié)點(diǎn)的值打印出來(lái)。

public static void extractKeyWordText(String url, String keyword) 

該方法生成針對(duì) String 類型的 url 變量代表的某個(gè)特定網(wǎng)頁(yè)的解析器,調(diào)用 1中的方法實(shí)現(xiàn)簡(jiǎn)單的遍歷。

清單 3 的代碼展示了如何迭代所有的網(wǎng)頁(yè),更多的工作可以在此基礎(chǔ)上展開(kāi)。比如找到某個(gè)特定的網(wǎng)頁(yè)內(nèi)部節(jié)點(diǎn),其實(shí)就可以

在遍歷所有的節(jié)點(diǎn)基礎(chǔ)上來(lái)判斷,看被迭代的節(jié)點(diǎn)是否滿足特定的需要。

使用 NodeFilter

NodeFilter 是一個(gè)接口,任何一個(gè)自定義的 Filter 都需要實(shí)現(xiàn)這個(gè)接口中的 boolean accept() 方法。如果希望迭代網(wǎng)頁(yè)節(jié)點(diǎn)的時(shí)候保留當(dāng)前節(jié)點(diǎn),則在節(jié)點(diǎn)條件滿足的情況下返回 true;否則返回 false。HtmlParse 里提供了很多實(shí)現(xiàn)了 NodeFilter 接口的類,下面就一些筆者所用到的,以及常用的 Filter 做一些介紹:

對(duì) Filter 做邏輯操作的 Fitler 有:AndFilter,NotFilter ,OrFilter,XorFilter。
這些 Filter 來(lái)組合不同的 Filter,形成滿足兩個(gè) Filter 邏輯關(guān)系結(jié)果的 Filter。

判斷節(jié)點(diǎn)的孩子,兄弟,以及父親節(jié)點(diǎn)情況的 Filter 有:HasChildFilterHasParentFilter,HasSiblingFilter。
判斷節(jié)點(diǎn)本身情況的 Filter 有 HasAttributeFilter:判讀節(jié)點(diǎn)是否有特定屬性;LinkStringFilter:判斷節(jié)點(diǎn)是否是具有特定模式 (pattern) url 的節(jié)點(diǎn);

TagNameFilter:判斷節(jié)點(diǎn)是否具有特定的名字;NodeClassFilter:判讀節(jié)點(diǎn)是否是某個(gè) HtmlParser 定義好的 Tag 類型。在 org.htmlparser.tags 包下有對(duì)應(yīng) Html標(biāo)簽的各種 Tag,例如 LinkTag,ImgeTag 等。

還有其他的一些 Filter 在這里不一一列舉了,可以在 org.htmlparser.filters 下找到。

清單 4 展示了如何使用上面提到過(guò)的一些 filter 來(lái)抽取網(wǎng)頁(yè)中的 <a> 標(biāo)簽里的 href屬性值,<img> 標(biāo)簽里的 src 屬性值,以及 <frame> 標(biāo)簽里的 src 的屬性值。

清單4

// 獲取一個(gè)網(wǎng)頁(yè)上所有的鏈接和圖片鏈接
public static void extracLinks(String url) {
    try {
      Parser parser = new Parser(url);
      parser.setEncoding("gb2312");
//過(guò)濾 <frame> 標(biāo)簽的 filter,用來(lái)提取 frame 標(biāo)簽里的 src 屬性所、表示的鏈接
      NodeFilter frameFilter = new NodeFilter() {
        public boolean accept(Node node) {
          if (node.getText().startsWith("frame src=")) {
            return true;
          } else {
            return false;
          }
        }
      };
    //OrFilter 來(lái)設(shè)置過(guò)濾 <a> 標(biāo)簽,<img> 標(biāo)簽和 <frame> 標(biāo)簽,三個(gè)標(biāo)簽是 or 的關(guān)系
   OrFilte rorFilter = new OrFilter(new NodeClassFilter(LinkTag.class), new
NodeClassFilter(ImageTag.class));
   OrFilter linkFilter = new OrFilter(orFilter, frameFilter);
  //得到所有經(jīng)過(guò)過(guò)濾的標(biāo)簽
  NodeList list = parser.extractAllNodesThatMatch(linkFilter);
  for (int i = 0; i < list.size(); i++) {
    Node tag = list.elementAt(i);
    if (tag instanceof LinkTag)//<a> 標(biāo)簽 
    {
      LinkTag link = (LinkTag) tag;
      String linkUrl = link.getLink();//url
      String text = link.getLinkText();//鏈接文字
      System.out.println(linkUrl + "**********" + text);
    }
    else if (tag instanceof ImageTag)//<img> 標(biāo)簽
    {
      ImageTag image = (ImageTag) list.elementAt(i);
      System.out.print(image.getImageURL() + "********");//圖片地址
      System.out.println(image.getText());//圖片文字
    }
    else//<frame> 標(biāo)簽
    {
//提取 frame 里 src 屬性的鏈接如 <frame src="test.html"/>
      String frame = tag.getText();
      int start = frame.indexOf("src=");
      frame = frame.substring(start);
      int end = frame.indexOf(" ");
      if (end == -1)
        end = frame.indexOf(">");
      frame = frame.substring(5, end - 1);
      System.out.println(frame);
    }
  }
} catch (ParserException e) {
      e.printStackTrace();
}
} 

簡(jiǎn)單強(qiáng)大的 StringBean
如果你想要網(wǎng)頁(yè)中去掉所有的標(biāo)簽后剩下的文本,那就是用 StringBean 吧。以下簡(jiǎn)單的代碼可以幫你解決這樣的問(wèn)題:

清單5

StringBean sb = new StringBean();
sb.setLinks(false);//設(shè)置結(jié)果中去點(diǎn)鏈接
sb.setURL(url);//設(shè)置你所需要濾掉網(wǎng)頁(yè)標(biāo)簽的頁(yè)面 url 
System.out.println(sb.getStrings());//打印結(jié)果 

HtmlParser 提供了強(qiáng)大的類庫(kù)來(lái)處理網(wǎng)頁(yè),由于本文旨在簡(jiǎn)單的介紹,因此只是將與筆者后續(xù)爬蟲(chóng)部分有關(guān)的關(guān)鍵類庫(kù)進(jìn)行了示例說(shuō)明。感興趣的讀者可以專門(mén)來(lái)研究一下 HtmlParser 更為強(qiáng)大的類庫(kù)。

簡(jiǎn)易爬蟲(chóng)的實(shí)現(xiàn)

HttpClient 提供了便利的 HTTP 協(xié)議訪問(wèn),使得我們可以很容易的得到某個(gè)網(wǎng)頁(yè)的源碼并保存在本地;HtmlParser 提供了如此簡(jiǎn)便靈巧的類庫(kù),可以從網(wǎng)頁(yè)中便捷的提取出指向其他網(wǎng)頁(yè)的超鏈接。筆者結(jié)合這兩個(gè)開(kāi)源包,構(gòu)建了一個(gè)簡(jiǎn)易的網(wǎng)絡(luò)爬蟲(chóng)。

爬蟲(chóng) (Crawler) 原理

學(xué)過(guò)數(shù)據(jù)結(jié)構(gòu)的讀者都知道有向圖這種數(shù)據(jù)結(jié)構(gòu)。如下圖所示,如果將網(wǎng)頁(yè)看成是圖中的某一個(gè)節(jié)點(diǎn),而將網(wǎng)頁(yè)中指向其他網(wǎng)頁(yè)的鏈接看成是這個(gè)節(jié)點(diǎn)指向其他節(jié)點(diǎn)的邊,那么我們很容易將整個(gè) Internet 上的網(wǎng)頁(yè)建模成一個(gè)有向圖。理論上,通過(guò)遍歷算法遍歷該圖,可以訪問(wèn)到Internet 上的幾乎所有的網(wǎng)頁(yè)。最簡(jiǎn)單的遍歷就是寬度優(yōu)先以及深度優(yōu)先。以下筆者實(shí)現(xiàn)的簡(jiǎn)易爬蟲(chóng)就是使用了寬度優(yōu)先的爬行策略

圖 2. 網(wǎng)頁(yè)關(guān)系的建模圖

簡(jiǎn)易爬蟲(chóng)實(shí)現(xiàn)流程

在看簡(jiǎn)易爬蟲(chóng)的實(shí)現(xiàn)代碼之前,先介紹一下簡(jiǎn)易爬蟲(chóng)爬取網(wǎng)頁(yè)的流程。

圖 3. 爬蟲(chóng)流程圖

各個(gè)類的源碼以及說(shuō)明

對(duì)應(yīng)上面的流程圖,簡(jiǎn)易爬蟲(chóng)由下面幾個(gè)類組成,各個(gè)類職責(zé)如下:

Crawler.java:爬蟲(chóng)的主方法入口所在的類,實(shí)現(xiàn)爬取的主要流程。

LinkDb.java:用來(lái)保存已經(jīng)訪問(wèn)的 url 和待爬取的 url 的類,提供url出對(duì)入隊(duì)操作。

Queue.java: 實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的隊(duì)列,在 LinkDb.java 中使用了此類。

FileDownloader.java:用來(lái)下載 url 所指向的網(wǎng)頁(yè)。

HtmlParserTool.java: 用來(lái)抽取出網(wǎng)頁(yè)中的鏈接。

LinkFilter.java:一個(gè)接口,實(shí)現(xiàn)其 accept() 方法用來(lái)對(duì)抽取的鏈接進(jìn)行過(guò)濾。

下面是各個(gè)類的源碼,代碼中的注釋有比較詳細(xì)的說(shuō)明。

清單6 Crawler.java

package com.ie;
 import java.util.Set;
public class Crawler {
  /* 使用種子 url 初始化 URL 隊(duì)列*/
  private void initCrawlerWithSeeds(String[] seeds)
  {
    for(int i=0;i<seeds.length;i++)
      LinkDB.addUnvisitedUrl(seeds[i]);
  }
   
  /* 爬取方法*/
  public void crawling(String[] seeds)
  {
    LinkFilter filter = new LinkFilter(){
      //提取以 http://www.twt.edu.cn 開(kāi)頭的鏈接
      public boolean accept(String url) {
        if(url.startsWith("http://www.twt.edu.cn"))
          return true;
        else
          return false;
      }
    };
    //初始化 URL 隊(duì)列
    initCrawlerWithSeeds(seeds);
    //循環(huán)條件:待抓取的鏈接不空且抓取的網(wǎng)頁(yè)不多于 1000
    while(!LinkDB.unVisitedUrlsEmpty()&&LinkDB.getVisitedUrlNum()<=1000)
    {
      //隊(duì)頭 URL 出對(duì)
      String visitUrl=LinkDB.unVisitedUrlDeQueue();
      if(visitUrl==null)
        continue;
      FileDownLoader downLoader=new FileDownLoader();
      //下載網(wǎng)頁(yè)
      downLoader.downloadFile(visitUrl);
      //該 url 放入到已訪問(wèn)的 URL 中
      LinkDB.addVisitedUrl(visitUrl);
      //提取出下載網(wǎng)頁(yè)中的 URL
       
      Set<String> links=HtmlParserTool.extracLinks(visitUrl,filter);
      //新的未訪問(wèn)的 URL 入隊(duì)
      for(String link:links)
      {
          LinkDB.addUnvisitedUrl(link);
      }
    }
  }
  //main 方法入口
  public static void main(String[]args)
  {
    Crawler crawler = new Crawler();
    crawler.crawling(new String[]{"http://www.twt.edu.cn"});
  }
}

清單7 LinkDb.java

package com.ie;
import java.util.HashSet;
import java.util.Set;
/**
 * 用來(lái)保存已經(jīng)訪問(wèn)過(guò) Url 和待訪問(wèn)的 Url 的類
 */
public class LinkDB {
  //已訪問(wèn)的 url 集合
  private static Set<String> visitedUrl = new HashSet<String>();
  //待訪問(wèn)的 url 集合
  private static Queue<String> unVisitedUrl = new Queue<String>();
  public static Queue<String> getUnVisitedUrl() {
    return unVisitedUrl;
  }
  public static void addVisitedUrl(String url) {
    visitedUrl.add(url);
  }
  public static void removeVisitedUrl(String url) {
    visitedUrl.remove(url);
  }
  public static String unVisitedUrlDeQueue() {
    return unVisitedUrl.deQueue();
  }
  // 保證每個(gè) url 只被訪問(wèn)一次
  public static void addUnvisitedUrl(String url) {
    if (url != null && !url.trim().equals("")
 && !visitedUrl.contains(url)
        && !unVisitedUrl.contians(url))
      unVisitedUrl.enQueue(url);
  }
  public static int getVisitedUrlNum() {
    return visitedUrl.size();
  }
  public static boolean unVisitedUrlsEmpty() {
    return unVisitedUrl.empty();
  }
}

清單8 Queue.java

package com.ie;
import java.util.LinkedList;
/**
 * 數(shù)據(jù)結(jié)構(gòu)隊(duì)列
 */
public class Queue<T> {
  private LinkedList<T> queue=new LinkedList<T>();
  public void enQueue(T t)
  {
    queue.addLast(t);
  }
  public T deQueue()
  {
    return queue.removeFirst();
  }
  public boolean isQueueEmpty()
  {
    return queue.isEmpty();
  }
  public boolean contians(T t)
  {
    return queue.contains(t);
  }
  public boolean empty()
  {
    return queue.isEmpty();
  }
} 

清單 9 FileDownLoader.java

package com.ie;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
public class FileDownLoader {
  /**根據(jù) url 和網(wǎng)頁(yè)類型生成需要保存的網(wǎng)頁(yè)的文件名
   *去除掉 url 中非文件名字符 
   */
  public String getFileNameByUrl(String url,String contentType)
  {
    url=url.substring(7);//remove http://
    if(contentType.indexOf("html")!=-1)//text/html
    {
      url= url.replaceAll("[\\?/:*|<>\"]", "_")+".html";
      return url;
    }
    else//如application/pdf
    {
return url.replaceAll("[\\?/:*|<>\"]", "_")+"."+ \
     contentType.substring(contentType.lastIndexOf("/")+1);
    }  
  }
  /**保存網(wǎng)頁(yè)字節(jié)數(shù)組到本地文件
   * filePath 為要保存的文件的相對(duì)地址
   */
  private void saveToLocal(byte[] data,String filePath)
  {
    try {
      DataOutputStream out=new DataOutputStream(
new FileOutputStream(new File(filePath)));
      for(int i=0;i<data.length;i++)
      out.write(data[i]);
      out.flush();
      out.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  /*下載 url 指向的網(wǎng)頁(yè)*/
  public String downloadFile(String url)
  {
     String filePath=null;
     /* 1.生成 HttpClinet 對(duì)象并設(shè)置參數(shù)*/
     HttpClient httpClient=new HttpClient();
     //設(shè)置 Http 連接超時(shí) 5s
        httpClient.getHttpConnectionManager().getParams().
setConnectionTimeout(5000);
     /*2.生成 GetMethod 對(duì)象并設(shè)置參數(shù)*/
     GetMethod getMethod=new GetMethod(url);   
     //設(shè)置 get 請(qǐng)求超時(shí) 5s
     getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT,5000);
     //設(shè)置請(qǐng)求重試處理
     getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
      new DefaultHttpMethodRetryHandler());
     /*3.執(zhí)行 HTTP GET 請(qǐng)求*/
     try{ 
       int statusCode = httpClient.executeMethod(getMethod);
       //判斷訪問(wèn)的狀態(tài)碼
       if (statusCode != HttpStatus.SC_OK) 
       {
System.err.println("Method failed: "+ getMethod.getStatusLine());
         filePath=null;
       }
       /*4.處理 HTTP 響應(yīng)內(nèi)容*/
 byte[] responseBody = getMethod.getResponseBody();//讀取為字節(jié)數(shù)組
       //根據(jù)網(wǎng)頁(yè) url 生成保存時(shí)的文件名
filePath="temp\\"+getFileNameByUrl(url,
      getMethod.getResponseHeader("Content-Type").getValue());
      saveToLocal(responseBody,filePath);
     } catch (HttpException e) {
          // 發(fā)生致命的異常,可能是協(xié)議不對(duì)或者返回的內(nèi)容有問(wèn)題
          System.out.println("Please check your provided http 
address!");
          e.printStackTrace();
         } catch (IOException e) {
          // 發(fā)生網(wǎng)絡(luò)異常
          e.printStackTrace();
         } finally {
          // 釋放連接
          getMethod.releaseConnection();      
         }
         return filePath;
  }
  //測(cè)試的 main 方法
  public static void main(String[]args)
  {
    FileDownLoader downLoader = new FileDownLoader();
    downLoader.downloadFile("http://www.twt.edu.cn");
  }
} 

清單 10 HtmlParserTool.java

package com.ie;
import java.util.HashSet;
import java.util.Set;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
public class HtmlParserTool {
  // 獲取一個(gè)網(wǎng)站上的鏈接,filter 用來(lái)過(guò)濾鏈接
  public static Set<String> extracLinks(String url,LinkFilter filter) {
    Set<String> links = new HashSet<String>();
    try {
      Parser parser = new Parser(url);
      parser.setEncoding("gb2312");
      // 過(guò)濾 <frame >標(biāo)簽的 filter,用來(lái)提取 frame 標(biāo)簽里的 src 屬性所表示的鏈接
      NodeFilter frameFilter = new NodeFilter() {
        public boolean accept(Node node) {
          if (node.getText().startsWith("frame src=")) {
            return true;
          } else {
            return false;
          }
        }
      };
      // OrFilter 來(lái)設(shè)置過(guò)濾 <a> 標(biāo)簽,和 <frame> 標(biāo)簽
      OrFilter linkFilter = new OrFilter(new NodeClassFilter(
          LinkTag.class), frameFilter);
      // 得到所有經(jīng)過(guò)過(guò)濾的標(biāo)簽
      NodeList list = parser.extractAllNodesThatMatch(linkFilter);
      for (int i = 0; i < list.size(); i++) {
        Node tag = list.elementAt(i);
        if (tag instanceof LinkTag)// <a> 標(biāo)簽
        {
          LinkTag link = (LinkTag) tag;
          String linkUrl = link.getLink();// url
          if(filter.accept(linkUrl))
            links.add(linkUrl);
        } else// <frame> 標(biāo)簽
        {
        // 提取 frame 里 src 屬性的鏈接如 <frame src="test.html"/>
          String frame = tag.getText();
          int start = frame.indexOf("src=");
          frame = frame.substring(start);
          int end = frame.indexOf(" ");
          if (end == -1)
            end = frame.indexOf(">");
          String frameUrl = frame.substring(5, end - 1);
          if(filter.accept(frameUrl))
            links.add(frameUrl);
        }
      }
    } catch (ParserException e) {
      e.printStackTrace();
    }
    return links;
  }
  //測(cè)試的 main 方法
  public static void main(String[]args)
  {
Set<String> links = HtmlParserTool.extracLinks(
"http://www.twt.edu.cn",new LinkFilter()
    {
      //提取以 http://www.twt.edu.cn 開(kāi)頭的鏈接
      public boolean accept(String url) {
        if(url.startsWith("http://www.twt.edu.cn"))
          return true;
        else
          return false;
      }
    });
    for(String link : links)
      System.out.println(link);
  }
} 

清單11 LinkFilter.java

package com.ie;
public interface LinkFilter {
  public boolean accept(String url);
} 

這些代碼中關(guān)鍵的部分都在 HttpClient 和 HtmlParser 介紹中說(shuō)明過(guò)了,其他部分也比較容易,請(qǐng)感興趣的讀者自行理解。

相關(guān)文章

  • 解決Mybatis查詢方法selectById()主鍵不一致問(wèn)題

    解決Mybatis查詢方法selectById()主鍵不一致問(wèn)題

    這篇文章主要介紹了解決Mybatis查詢方法selectById()主鍵不一致問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • SpringBoot中的Bean注入問(wèn)題

    SpringBoot中的Bean注入問(wèn)題

    SpringBoot開(kāi)發(fā)中,Bean注入是關(guān)鍵,涉及構(gòu)造函數(shù)注入、Setter注入和字段注入等方法,常見(jiàn)問(wèn)題包括Bean未找到、循環(huán)依賴、多個(gè)實(shí)現(xiàn)注入等,推薦使用構(gòu)造函數(shù)注入以增強(qiáng)代碼測(cè)試性和維護(hù)性,并關(guān)注Bean的生命周期和作用域
    2024-09-09
  • java如何實(shí)現(xiàn)圖片轉(zhuǎn)化為數(shù)據(jù)流

    java如何實(shí)現(xiàn)圖片轉(zhuǎn)化為數(shù)據(jù)流

    這篇文章主要介紹了java如何實(shí)現(xiàn)圖片轉(zhuǎn)化為數(shù)據(jù)流,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java流程控制語(yǔ)句之If選擇結(jié)構(gòu)

    Java流程控制語(yǔ)句之If選擇結(jié)構(gòu)

    今天繼續(xù)帶大家復(fù)習(xí)Java流程控制語(yǔ)句的相關(guān)知識(shí),本文對(duì)If選擇結(jié)構(gòu)作了非常詳細(xì)的介紹及代碼示例,對(duì)正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • ThreadPoolExecutor核心線程數(shù)和RocketMQ消費(fèi)線程調(diào)整詳解

    ThreadPoolExecutor核心線程數(shù)和RocketMQ消費(fèi)線程調(diào)整詳解

    這篇文章主要介紹了ThreadPoolExecutor核心線程數(shù)和RocketMQ消費(fèi)線程調(diào)整詳解,Rocketmq 消費(fèi)者在高峰期希望手動(dòng)減少消費(fèi)線程數(shù),通過(guò)DefaultMQPushConsumer.updateCorePoolSize方法可以調(diào)用內(nèi)部的setCorePoolSize設(shè)置多線程核心線程數(shù),需要的朋友可以參考下
    2023-10-10
  • Spring 中優(yōu)雅的獲取泛型信息的方法

    Spring 中優(yōu)雅的獲取泛型信息的方法

    這篇文章主要介紹了Spring 中優(yōu)雅的獲取泛型信息的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java十分鐘精通異常處理機(jī)制

    Java十分鐘精通異常處理機(jī)制

    異常就是不正常,比如當(dāng)我們身體出現(xiàn)了異常我們會(huì)根據(jù)身體情況選擇喝開(kāi)水、吃藥、看病、等?異常處理方法。?java異常處理機(jī)制是我們java語(yǔ)言使用異常處理機(jī)制為程序提供了錯(cuò)誤處理的能力,程序出現(xiàn)的錯(cuò)誤,程序可以安全的退出,以保證程序正常的運(yùn)行等
    2022-03-03
  • Java?詳解Map集合之HashMap和TreeMap

    Java?詳解Map集合之HashMap和TreeMap

    本章具體介紹了HashMap、TreeMap兩種集合的基本使用方法和區(qū)別,圖解穿插代碼實(shí)現(xiàn)。?JAVA成仙路從基礎(chǔ)開(kāi)始講,后續(xù)會(huì)講到JAVA高級(jí),中間會(huì)穿插面試題和項(xiàng)目實(shí)戰(zhàn),希望能給大家?guī)?lái)幫助
    2022-03-03
  • SpringBoot如何實(shí)現(xiàn)分離資源文件并打包

    SpringBoot如何實(shí)現(xiàn)分離資源文件并打包

    這篇文章主要介紹了SpringBoot如何實(shí)現(xiàn)分離資源文件并打包,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Java調(diào)用外接設(shè)備詳解(制卡機(jī))

    Java調(diào)用外接設(shè)備詳解(制卡機(jī))

    這篇文章主要為大家詳細(xì)介紹了Java調(diào)用外接設(shè)備的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07

最新評(píng)論

娄烦县| 黑河市| 江北区| 巨鹿县| 鹤山市| 台北县| 新建县| 梓潼县| 吴旗县| 平顺县| 汾西县| 安福县| 和静县| 大渡口区| 大丰市| 台中县| 峨边| 文成县| 成都市| 云浮市| 桦川县| 五原县| 广宁县| 西青区| 贺兰县| 安平县| 家居| 门头沟区| 富平县| 梁平县| 那曲县| 安阳市| 四平市| 黄平县| 秀山| 石泉县| 理塘县| 澜沧| 施甸县| 天峨县| 汉寿县|