httpclient 抓取网页(如何开发一个Java爬虫(的设计机制及原理))

优采云 发布时间: 2022-04-07 09:29

  httpclient 抓取网页(如何开发一个Java爬虫(的设计机制及原理))

  最近在写一个小爬虫,准备爬取一部分网页数据进行模型训练。在考虑如何爬取和分析网页的时候,参考了OSC站的一些项目,特别是@黄一华写的webmagic的设计机制和原理——如何开发Java爬虫”文章给了很多启发,webmagic是一个垂直爬虫,而我想写的是一个比较通用的爬虫,主要是爬取中文网站对于HTTP协议和消息处理,没有比HttpClient组件更好的选择。对于HTML代码解析,后对比HTMLParser和Jsoup,后者在API的使用上优势明显,简洁易懂,在确定使用的开源组件后,

  我的爬虫要爬取这部分功能,我只需要根据网页的URL爬取HTML代码,然后从HTML代码中解析出链接和HTML代码。

  可以使用标签的文本,所以解析的结果可以用一个Page类来表示,纯粹是一个POJO,那么如何使用HttpClient和Jsoup直接解析成Page对象呢?

  在HttpClient4.2中,提供了ResponseHandler接口,负责处理HttpResponse。因此,通过实现该接口,可以将返回的 HTML 代码解析为所需的 Page 对象。主要思路是先将HttpResponse中的数据读出来,转换成HTML代码,然后用jsoup解析

  标签和标签。代码显示如下,

  公共类 PageResponseHandler 实现 ResponseHandler

  {

  private Page page;

public PageResponseHandler(Page page) {

this.page = page;

}

public void setPage(Page page) {

this.page = page;

}

public Page getPage() {

return page;

}

@Override

public Page handleResponse(HttpResponse response) throws ClientProtocolException, IOException {

StatusLine statusLine = response.getStatusLine();

HttpEntity entity = response.getEntity();

if (statusLine.getStatusCode() >= 300) {

EntityUtils.consume(entity);

throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());

}

if (entity == null)

return null;

// 利用HTTPClient自带的EntityUtils把当前HttpResponse中的HttpEntity转化成HTML代码

String html = EntityUtils.toString(entity);

Document document = Jsoup.parse(html);

Elements links = document.getElementsByTag("a");

for (int i = 0; i < links.size(); i++) {

Element link = links.get(i);

page.addAnchor(link.attr("href"), link.text());

}

// parse context of plain text from HTML code,

Elements paragraphs = document.getElementsByTag("p");

StringBuffer plainText = new StringBuffer(html.length() / 2);

for (int i = 0; i < paragraphs.size(); i++) {

Element paragraph = paragraphs.get(i);

plainText.append(paragraph.text()).append("\n");

}

page.setPlainText(plainText.toString());

return page;

}

  }

  代码不超过40行,非常简单。现在可以直接返回Page对象,编写一个测试类来测试这个PageResponseHandler。测试这个类的功能不需要复杂的代码。

  公共类 PageResponseHandlerTest {

  HttpClient httpclient;

PageResponseHandler pageResponseHandler;

final String url = "http://news.163.com/13/0903/11/97RHS2NS0001121M.html";

Page page = new Page(url);

@Before

public void setUp() throws Exception {

httpclient = new DefaultHttpClient();

HttpGet httpget = new HttpGet(url);

pageResponseHandler = new PageResponseHandler(page);

httpclient.execute(httpget, pageResponseHandler);

}

@After

public void tearDown() throws Exception {

httpclient.getConnectionManager().shutdown();

}

@Test

public void test() {

System.out.println(page.getPlainText());

assertTrue(page.getPlainText().length() > 0);

assertTrue(page.getAnchors().size() > 0);

}

  }

  到目前为止,该爬虫中的爬取和分析功能运行良好。对于中文,它们也可以很好地解析。比较细心的读者会看到,这些代码中并没有设置字符集,也没有对字符集进行转换,后面会讲到HttpClient4.2组件中的字符集处理。

  让我们首先回顾一下 Content-Type 在 HTTP 协议的 RFC 规范中的作用。它指示发送给接收方 HttpEntity 的内容的媒体类型。对于text-type HttpEntity,通常采用如下形式,指定HttpEntity的媒体类型,使用另外,RFC规范还规定在Content-Type不指定字符集的情况下,ISO-8859-默认使用 1 个字符集来编码 Http Entity

  1

  内容类型:文本/html;字符集=UTF-8

  说到这里,你应该可以猜到 HttpClient 4.2 是如何正确编码的——即使用 Content-Type 标头中收录的字符集作为输入源进行编码。具体代码见EntityUtils类第212行开始的代码。EntityUtils 首先从 HttpEntity 对象中获取 Content-Type。如果 Content-Type 的字符集不为空,则使用 Content-Type 对象中指定的字符集进行编码,否则使用开发者指定的字符集进行编码,如果开发者未指定字符集,默认字符集 iso-8859-1 用于编码。当然,编码实现是调用JDK的Reader类。

  ContentType contentType = ContentType.getOrDefault(entity);

  字符集 charset = contentType.getCharset();

  if (charset == null) {

  字符集 = 默认字符集;

  }

  if (charset == null) {

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线