文档章节

如何使用HttpClient和Jsoup抓取网页及分析网页

M
 Miracle_lucy
发布于 2014/06/10 08:33
字数 1372
阅读 89
收藏 1

最近在写一个小爬虫,准备爬一部分网页数据,来做模型训练,在考虑如何抓取网页及分析网页时,参考了OSC站中一些项目,特别是@黄亿华写的《webmagic的设计机制及原理-如何开发一个Java爬虫》这篇文章给了不少启发,webmagic是一个垂直的爬虫,而我要写的是一个比较通用的爬虫,主要爬起中文的网站的内容,对于HTTP协议及报文的处理,没有比HttpClient组件更好的选择了,对于HTML代码的解析,在比较HTMLParser和Jsoup后,后者在API的使用上优势明显,简洁易懂。所使用的开源组件定下来后,接着开始思考如何抓取和解析这两个基本功能。

对于我的爬虫抓取这部分功能来说,只要根据网页的URL抓取HTML代码,再从HTML代码中的解析出链接和<p>标签的文本即可以,因此解析的结果可以用一个Page类来表示,这个类纯粹是一个POJO,那么如何使用HttpClient和Jsoup直接解析成Page对象?

在HttpClient 4.2中,提供了ResponseHandler<T>这个接口来负责处理HttpResponse,因此,借助实现这个接口,可以所返回的HTML代码解析成所需要的Page对象,主要的思路是先把HttpResponse中的数据读出来,变换成HTML代码,然后再用jsoup解析<p>标签和<a>标签。代码如下,

01 public class PageResponseHandler implements ResponseHandler<Page> {
02  
03     private Page page;
04  
05     public PageResponseHandler(Page page) {
06     this.page = page;
07     }
08  
09     public void setPage(Page page) {
10     this.page = page;
11     }
12  
13     public Page getPage() {
14     return page;
15     }
16  
17     @Override
18     public Page handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
19  
20     StatusLine statusLine = response.getStatusLine();
21     HttpEntity entity = response.getEntity();
22  
23     if (statusLine.getStatusCode() >= 300) {
24         EntityUtils.consume(entity);
25         throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
26     }
27  
28     if (entity == null)
29         return null;
30  
31     // 利用HTTPClient自带的EntityUtils把当前HttpResponse中的HttpEntity转化成HTML代码
32     String html = EntityUtils.toString(entity);
33  
34  
35     Document document = Jsoup.parse(html);
36     Elements links = document.getElementsByTag("a");
37  
38     for (int i = 0; i < links.size(); i++) {
39         Element link = links.get(i);
40         page.addAnchor(link.attr("href"), link.text());
41     }
42  
43     // parse context of plain text from HTML code,
44     Elements paragraphs = document.getElementsByTag("p");
45     StringBuffer plainText = new StringBuffer(html.length() / 2);
46     for (int i = 0; i < paragraphs.size(); i++) {
47         Element paragraph = paragraphs.get(i);
48         plainText.append(paragraph.text()).append("\n");
49     }
50     page.setPlainText(plainText.toString());
51  
52     return page;
53     }
54  
55 }
代码不超过40行,非常简单,现在可以直接返回Page对象了,写一个测试类,来测试这个PageResponseHandler.测试这个类的功能也不需要复杂的代码。
01 public class PageResponseHandlerTest {
02  
03     HttpClient httpclient;
04  
05     PageResponseHandler pageResponseHandler;
06  
07     final String url = "http://news.163.com/13/0903/11/97RHS2NS0001121M.html";
08  
09     Page page = new Page(url);
10  
11     @Before
12     public void setUp() throws Exception {
13     httpclient = new DefaultHttpClient();
14     HttpGet httpget = new HttpGet(url);
15     pageResponseHandler = new PageResponseHandler(page);
16     httpclient.execute(httpget, pageResponseHandler);
17     }
18  
19     @After
20     public void tearDown() throws Exception {
21     httpclient.getConnectionManager().shutdown();
22     }
23  
24     @Test
25     public void test() {
26     System.out.println(page.getPlainText());
27     assertTrue(page.getPlainText().length() > 0);
28     assertTrue(page.getAnchors().size() > 0);
29     }
30 }
到目前为止,这个爬虫中的抓取和分析功能部分都工作的很好,对于中文,也能很好的解析,比较细心的读者会看到,这些代码中并没有设置字符集,也没有进行字符集进行转换,稍后会讲到HttpClient 4.2组件中字符集的处理。

先回顾一下关于HTTP协议的RFC规范中Content-Type作用,它指明发送给接收者Http Entity内容的媒介类型,对于文本类型的HttpEntity而言,它通常是下面的形式,指定HttpEntity的媒介类型,使用了何种字符集进行编码的,此外RFC规范还规定了,在Content-Type没有指定字符集的情况,默认使用ISO-8859-1字符集对Http Entity进行编码

1 Content-Type: text/html; charset=UTF-8
说到这里,大家应该能猜到HttpClient 4.2是如何能正确的进行编码的----就是利用Content-Type头中所包含的字符集作为编码的输入源。具体的代码可以看EntityUtils这个类的第212行开始的代码。EntityUtils首先从HttpEntity对象中获取Content-Type, 如果Content-Type的字符集不为空,则使用Content-Type对象中指定的字符集进行编码,否则使用开发者指定的字符集进行编码,如果开发者也没有指定字符集,使用默认的字符集iso-8859-1进行编码,当然编码实现,还是调用JDK的Reader类。
01 ContentType contentType = ContentType.getOrDefault(entity);
02             Charset charset = contentType.getCharset();
03             if (charset == null) {
04                 charset = defaultCharset;
05             }
06             if (charset == null) {
07                 charset = HTTP.DEF_CONTENT_CHARSET;
08             }
09             Reader reader = new InputStreamReader(instream, charset);
10             CharArrayBuffer buffer = new CharArrayBuffer(i);
11             char[] tmp = new char[1024];
12             int l;
13             while((l = reader.read(tmp)) != -1) {
14                 buffer.append(tmp, 0, l);
15             }
16             return buffer.toString();

开放的互联网造就繁荣的网站的同时,也给非遵循HTML规范的网站一些机会,有一些中文网站,没有正确的设置HTTP请求相应头中的Content-Type属性,导致HttpClient用默认的iso-8859-1字符集对Http Entity内进行编码,因此为了防止HttpClient在爬取中文网站出现乱码的问题,可以指定一个默认的GBK字符集,对原有的PageResponseHandler中的那行转换字符串的代码修改成

1 String html = EntityUtils.toString(entity, Charset.forName("gbk"));
至此,爬虫的爬取和解析功能都完成了,再也不怕没有正确设置Content-Type头的中文网站。希望这个篇文章能对读者有用。

@仪山湖

本文转载自:http://www.oschina.net/question/564772_124359

共有 人打赏支持
M
粉丝 0
博文 5
码字总数 0
作品 0
桂林
爬虫以及爬虫如何解决ip封锁问题的探究

一、简介 网络爬虫,是一种自动获取网页内容的程序。是搜索引擎的重要组成部分,因此搜索引擎优化很大程度上就是针对爬虫而做出的优化。【从别人的网站爬取有用数据到自己本地数据库】 网络爬...

谢思华
2013/12/03
0
1
爬虫--[HttpClient]

爬虫技术可以获取互联网上开放的网页文档或其他文档,在java中HttpClient是比较好用的模拟请求和爬虫组件 下面看一个简单的职位爬去的实例: 1 下载HttpClient 最新HttpClient版本是4.x,我们...

Candy_Desire
2014/11/06
0
0
java HttpClient+Jsoup打造灌水利器再也不怕起火了

不知道多久以前就有过写个自动回帖的小软件一直没有实现,最近闲下来了遂研究了下,本人小菜对于HTTP协议一知半解只能在请教google大神了,把我的想法跟google大神说了之后,google大神说这小...

小泼皮
2014/02/12
0
0
HttpClient的CircularRedirectException异常原因及解决办法

HttpClient的CircularRedirectException异常原因及解决办法 这两天在使用我自己爬虫抓取网页的时候总是出现 org.apache.http.client.ClientProtocolException at org.apache.http.impl.clien...

我是小强
2013/12/26
0
0
社会化海量数据采集爬虫框架搭建

随着BIG DATA大数据概念逐渐升温,如何搭建一个能够采集海量数据的架构体系摆在大家眼前。如何能够做到所见即所得的无阻拦式采集、如何快速把不规则页面结构化并存储、如何满足越来越多的数据...

观澜而索源
2013/07/27
0
1

没有更多内容

加载失败,请刷新页面

加载更多

实战讲解高并发和秒杀抢购系统设计

互联网特别是电商平台,阿里双11秒杀、还有12306春运抢票、以及平时各种节假日抢购活动等,都是典型的高并发场景。 这类场景最大的特征就是活动周期短,瞬间流量大(高并发),大量的人短期涌...

xtof
8分钟前
0
0
代码质量管理平台-sonarqube

在工作中,往往开发的时候会不怎么注重代码质量的人很多,存在着很多的漏洞和隐患等问题,sonarqube可以进行代码质量的审核,而且十分的残酷。。。。。接下来我们说下怎么安装 进入官网下载:...

落叶清风
11分钟前
4
0
在Ubuntu安装和配置Sphinx

Ubuntu系统默认是配置有sphinx的,先检查一下,别多此一举。。。。。 在开始本指南之前,您需要: 一个Ubuntu 16.04服务器。 sudo的一个非root用户,您可以通过以下设置本教程 。 安装在服务...

阿锋zxf
20分钟前
0
0
Qt编写输入法V2018超级终结版

对于qt嵌入式linux开发人员来说,输入法一直是个鸡肋问题,要么不支持实体键盘同步,要么不能汉字输入,要么不支持网页输入等,这几年通过陆续接触大量的各种输入法应用场景客户,得到真实需...

飞扬青云
31分钟前
0
0
TypeScript基础入门之高级类型的多态的 this类型

转发 TypeScript基础入门之高级类型的多态的 this类型 高级类型 多态的this类型 多态的this类型表示的是某个包含类或接口的子类型。 这被称做F-bounded多态性。 它能很容易的表现连贯接口间的...

durban
37分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部