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

优采云 发布时间: 2021-10-18 13:11

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

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

  对于我的爬虫爬取部分功能,只要根据网页的url爬取html代码,然后从html代码中解析出link和link

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

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

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

  public class PageResponseHandler implements 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。测试这个类的功能不需要复杂的代码。

  public class 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规范中的作用。它指定发送给接收者的 Http 实体内容的媒体类型。对于文本类型HttpEntity,通常采用如下形式。指定 HttpEntity 的媒体类型。使用哪个字符集用于编码?另外,RFC规范还规定,如果Content-Type没有指定字符集,则默认使用ISO-8859-1字符集对Http实体进行编码

  Content-Type: text/html; charset=UTF-8

  说了这么多,大家应该都能猜到HttpClient4.2是如何正确编码的----就是使用Content-Type头中收录的字符集作为编码输入源。具体代码可以看EntityUtils类第212行开始的代码。EntityUtils 首先从 HttpEntity 对象获取 Content-Type。如果Content-Type的字符集不为空,则使用Content-Type对象中指定的字符集进行编码,否则使用开发者指定的字符集进行编码。字符集也没有指定,使用默认的字符集iso-8859-1进行编码。当然是实现了编码,还是调用了JDK的Reader类。

  ContentType contentType = ContentType.getOrDefault(entity);

Charset charset = contentType.getCharset();

if (charset == null) {

charset = defaultCharset;

}

if (charset == null) {

charset = HTTP.DEF_CONTENT_CHARSET;

}

Reader reader = new InputStreamReader(instream, charset);

CharArrayBuffer buffer = new CharArrayBuffer(i);

char[] tmp = new char[1024];

int l;

while((l = reader.read(tmp)) != -1) {

buffer.append(tmp, 0, l);

}

return buffer.toString();

  开放的互联网在创造了繁荣的网站的同时,也为非HTML规范网站提供了一些机会。部分中文网站没有正确设置HTTP请求对应头部的Content-。Type 属性使 HttpClient 使用默认的 iso-8859-1 字符集对 Http 实体进行编码。所以为了防止HttpClient抓取中文网站,可以指定一个默认的GBK字符集。将原来的 PageResponseHandler 转换字符串的代码行修改为

  String html = EntityUtils.toString(entity, Charset.forName("gbk"));

  至此,爬虫的爬取解析功能已经完成,再也不怕Content-Type头的中文网站设置不正确了。希望这篇文章文章对读者有用。

  @仪山湖

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线