- 1. WebMagic 概览
- 1.1 设计思想
- 1.2 总体架构
- 1.3 项目组成
- 2. 快速开始
- 2.1 使用Maven
- 2.2 不使用Maven
- 2.3 第一个爬虫项目
- 3. 下载和编译源码
- 3.1 下载源码
- 3.2 导入项目
- 3.3 编译和执行源码
- 4. 编写基本的爬虫
- 4.1 实现 PageProcessor
- 4.2 使用 Selectable 抽取元素
- 4.3 使用Pipeline保存结果
- 4.4 爬虫的配置、启动和终止
- 4.5 Jsoup与Xsoup
- 4.6 爬虫的监控
- 4.7 配置代理
- 4.8 处理非 HTTP GET 请求
- 5. 使用注解编写爬虫
- 5.1 编写Model类
- 5.2 TargetUrl与HelpUrl
- 5.3 使用ExtractBy进行抽取
- 5.4 在类上使用ExtractBy
- 5.5 结果的类型转换
- 5.6 一个完整的流程
- 5.7 AfterExtractor
- 6. 组件的使用和定制
- 6.1 使用和定制 Pipeline
- 6.2 使用和定制 Scheduler
- 6.3 使用和定制 Downloader
- 附录:实例分析
- 列表 + 详情的基本页面组合
- 抓取前端渲染的页面
抓取前端渲染的页面
随着 AJAX 技术不断的普及,以及现在 AngularJS 这种 Single-page application 框架的出现,现在 js 渲染出的页面越来越多。对于爬虫来说,这种页面是比较讨厌的:仅仅提取 HTML 内容,往往无法拿到有效的信息。那么如何处理这种页面呢?总的来说有两种做法:
- 在抓取阶段,在爬虫中内置一个浏览器内核,执行 js 渲染页面后,再抓取。这方面对应的工具有
Selenium
、HtmlUnit
或者PhantomJs
。但是这些工具都存在一定的效率问题,同时也不是那么稳定。好处是编写规则同静态页面一样。 - 因为 js 渲染页面的数据也是从后端拿到,而且基本上都是 AJAX 获取,所以分析 AJAX 请求,找到对应数据的请求,也是比较可行的做法。而且相对于页面样式,这种接口变化可能性更小。缺点就是找到这个请求,并进行模拟,是一个相对困难的过程,也需要相对多的分析经验。
对比两种方式,我的观点是,对于一次性或者小规模的需求,用第一种方式省时省力。但是对于长期性的、大规模的需求,还是第二种会更靠谱一些。对于一些站点,甚至还有一些 js 混淆的技术,这个时候,第一种的方式基本是万能的,而第二种就会很复杂了。
对于第一种方法, webmagic-selenium
就是这样的一个尝试,它定义了一个 Downloader
,在下载页面时,就是用浏览器内核进行渲染。selenium 的配置比较复杂,而且跟平台和版本有关,没有太稳定的方案。感兴趣的可以看我这篇博客: 使用 Selenium 来抓取动态加载的页面
这里我主要介绍第二种方法,希望到最后你会发现:原来解析一个前端渲染的页面,也没有那么复杂。这里我们以 AngularJS 中文社区 http://angularjs.cn/ 为例。
1 如何判断前端渲染
判断页面是否为 js 渲染的方式比较简单,在浏览器中直接查看源码(Windows 下 Ctrl+U,Mac 下 command+alt+u),如果找不到有效的信息,则基本可以肯定为 js 渲染。
这个例子中,在页面中的标题 有孚计算机网络-前端攻城师
在源码中无法找到,则可以断定是 js 渲染,并且这个数据是 AJAX 得到。
2 分析请求
下面我们进入最难的一部分:找到这个数据请求。这一步能帮助我们的工具,主要是浏览器中查看网络请求的开发者工具。
以 Chome 为例,我们打开 开发者工具(Windows 下是 F12,Mac 下是 command+alt+i),然后重新刷新页面(也有可能是下拉页面,总之是所有你认为可能触发新数据的操作),然后记得保留现场,把请求一个个拿来分析吧!
这一步需要一点耐心,但是也并不是无章可循。首先能帮助我们的是上方的分类筛选(All、Document 等选项)。如果是正常的 AJAX,在 XHR
标签下会显示,而 JSONP 请求会在 Scripts
标签下,这是两个比较常见的数据类型。
然后你可以根据数据大小来判断一下,一般结果体积较大的更有可能是返回数据的接口。剩下的,基本靠经验了,例如这里这个 latest?p=1&s=20
一看就很可疑…
对于可疑的地址,这时候可以看一下响应体是什么内容了。这里在开发者工具看不清楚,我们把 URL http://angularjs.cn/api/article/latest?p=1&s=20
复制到地址栏,重新请求一次(如果用 Chrome 推荐装个 jsonviewer,查看 AJAX 结果很方便)。查看结果,看来我们找到了想要的。
同样的办法,我们进入到帖子详情页,找到了具体内容的请求: http://angularjs.cn/api/article/A0y2
。
3 编写程序
回想一下之前列表+目标页的例子,会发现我们这次的需求,跟之前是类似的,只不过换成了 AJAX 方式-AJAX 方式的列表,AJAX 方式的数据,而返回数据变成了 JSON。那么,我们仍然可以用上次的方式,分为两种页面来进行编写:
数据列表
在这个列表页,我们需要找到有效的信息,来帮助我们构建目标 AJAX 的 URL。这里我们看到,这个 _id
应该就是我们想要的帖子的 id,而帖子的详情请求,就是由一些固定 URL 加上这个 id 组成。所以在这一步,我们自己手动构造 URL,并加入到待抓取队列中。这里我们使用 JsonPath 这种选择语言来选择数据(webmagic-extension 包中提供了 JsonPathSelector
来支持它)。
if (page.getUrl().regex(LIST_URL).match()) {
//这里我们使用 JSONPATH 这种选择语言来选择数据
List<String> ids = new JsonPathSelector("$.data[*]._id").selectList(page.getRawText());
if (CollectionUtils.isNotEmpty(ids)) {
for (String id : ids) {
page.addTargetRequest("http://angularjs.cn/api/article/"+id);
}
}
}
目标数据
有了 URL,实际上解析目标数据就非常简单了,因为 JSON 数据是完全结构化的,所以省去了我们分析页面,编写 XPath 的过程。这里我们依然使用 JsonPath 来获取标题和内容。
page.putField("title", new JsonPathSelector("$.data.title").select(page.getRawText()));
page.putField("content", new JsonPathSelector("$.data.content").select(page.getRawText()));
这个例子完整的代码请看 AngularJSProcessor.java
4 总结
在这个例子中,我们分析了一个比较经典的动态页面的抓取过程。实际上,动态页面抓取,最大的区别在于:它提高了链接发现的难度。我们对比一下两种开发模式:
- 后端渲染的页面:下载辅助页面=>发现链接=>下载并分析目标 HTML
- 前端渲染的页面:发现辅助数据=>构造链接=>下载并分析目标 AJAX
对于不同的站点,这个辅助数据可能是在页面 HTML 中已经预先输出,也可能是通过 AJAX 去请求,甚至可能是多次数据请求的过程,但是这个模式基本是固定的。
但是这些数据请求的分析比起页面分析来说,仍然是要复杂得多,所以这其实是动态页面抓取的难点。
本节这个例子希望做到的是,在分析出请求后,为这类爬虫的编写提供一个可遵循的模式,即 发现辅助数据=>构造链接=>下载并分析目标 AJAX
这个模式。
WebMagic 0.5.0 之后会将 Json 的支持增加到链式 API 中,以后你可以使用:
page.getJson().jsonPath("$.name").get();
这样的方式来解析 AJAX 请求了。同时也支持:
page.getJson().removePadding("callback").jsonPath("$.name").get();
这样的方式来解析 JSONP 请求。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论