js 爬虫抓取网页数据(AngularJSjs渲染出的页面越来越多如何判断前端渲染页面)
优采云 发布时间: 2022-03-25 05:28js 爬虫抓取网页数据(AngularJSjs渲染出的页面越来越多如何判断前端渲染页面)
随着AJAX技术的不断普及和AngularJS等单页应用框架的出现,越来越多的页面使用js渲染。对于爬虫来说,这种页面比较烦人:只提取 HTML 内容,往往无法获取有效信息。那么如何处理这种页面呢?一般有两种方法:
爬取阶段,爬虫内置浏览器内核,爬取前执行js渲染页面。这方面的相应工具是 Selenium、HtmlUnit 或 PhantomJs。但这些工具都存在一定的效率问题,同时也不是那么稳定。优点是编写规则与静态页面相同。因为js渲染页面的数据也是从后端获取的,而且基本上是通过AJAX获取的,所以分析AJAX请求,找到数据对应的请求也是可行的。而且相对于页面样式,这个界面是不太可能改变的。缺点是找到这个请求并模拟它是一个比较困难的过程,需要比较大量的分析经验。
比较这两种方法,我的观点是,对于一次性或小规模的需求,第一种方法省时省力。但对于长期、*敏*感*词*的需求,第二种更为可靠。对于某些网站,甚至还有一些 js 混淆技术。这时候第一种方法基本上是万能的,而第二种方法会很复杂。
对于第一种方法,webmagic-selenium就是这样一种尝试,它定义了一个Downloader,它在下载页面时使用浏览器内核进行渲染。selenium的配置比较复杂,和平台和版本有关,没有稳定的解决方案。有兴趣可以看我的博客:使用Selenium爬取动态加载的页面
这里我主要介绍第二种方法。我希望你会发现解析一个前端渲染的页面并没有那么复杂。这里我们以AngularJS中文社区为例。
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复制到地址栏再请求一次(如果你用Chrome推荐安装一个jsonviewer,查看AJAX结果很方便)。查看结果,看起来我们找到了我们想要的东西。
同样的方法,我们进入帖子详情页面,找到具体内容的请求:.
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 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 模式。
PS:
WebMagic 0.5.0 稍后会为链式 API 添加 Json 支持,您可以使用:
page.getJson().jsonPath("$.name").get();
这种方式来解析 AJAX 请求。
还支持
page.getJson().removePadding("callback").jsonPath("$.name").get();
这种方式来解析 JSONP 请求。