nodejs爬虫笔记(四)---利用nightmare解决加载更多问题
目标:
解决页面加载更多问题。笔记三中,我们只爬取到网页的部分信息,而点击加载更多后的页面内容是没有提取到的。开始我的想法是找到加载更多的数据接口(可参照:http://www.jianshu.com/p/3fdb6ab47aef),但是我又发现一个问题,当我打开一个订阅号页面时,找到数据接口如下图,点击response会发现里面有相应的内容,对其进行解析时得到的内容却是空的,也就是说我得不到页面的信息。而且我发现有些网页的数据接口是加密的,根本访问不到。因此,我又只能换种思路,看能不能模仿浏览器进行点击,等加载更多没有的时候我再去解析页面,是不是就能够获取到所有页面信息呢。我开始找到了PhantomJS这个模块,但是运行的时候速度有点慢,后面和同学一起又找到了一个更好的模块---nightmare。
一、nightmare
1、简单介绍
nightmare是PhantomJS的高级封装,让你能够实现浏览器自动化任务。PhantomJS 是一个基于WebKit的服务器端 JavaScript API。它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。PhantomJS可以用于页面自动化,网络监测,网页截屏,以及无界面测试等。下面再贴一段官网介绍。我们可以看到速度是phantomJs的两倍,这正是我们想要的。
2、应用时需要注意的地方
首先肯定是要安装啦,安装介绍我就不废话了,可以参考nightmare官方文档,还有一些常用的api,如type输入操作,click点击操作等等。具体可以参考»。下面我只提一下几个需要注意的地方:
(1)evaluate方法,方法的参数函数的执行环境是浏览器环境,而不是脚本的运行环境,因此,其内部无法访问到脚本内定义的全局变量,但是可以通过evaluate的后续参数,来作为第一参数函数的参数传入,虽说如此,但是!!!这种方法不能传一些特殊对象,比如原型链上的内容,是不会带上的,而且即使说node环境有Date对象,浏览器也有Date对象,也不能直接传入一个Date的实例,传入之后,也用不了Date的内置方法。例如:下面的例子中我将定义的数组arr传入给了evaluate方法。
var arr = [];
nightmare //加载页面 .goto('http://www.baidu.com') //等待选择器加载完毕,可以用数值(表示等待时间,单位毫秒) .wait('body') //浏览器内页面执行 .evaluate(function(arr){ //这里外部的变量hello以参数的形式传了进来,可以用content获得。 var p = document.querySelector('#cp').innerText; arr.push(p); return arr; }, arr) //结束操作 .end() //前面都是操作队列,需要有then方法才会触发执行上述队列的操作 .then(function(res){//函数参数为evaluate的返回值 console.log(res); }) //处理异常情况 .catch(function (error) { console.error('failed:', error); });
(2)在使用 nightmare 的时候,如果在忘记了在代码的最后调用 then 方法,会发现 nightmare 不会执行任何操作。例如执行下面这段代码,在打开浏览窗口之后不会有任何操作结果。
1
2
3
4
5
|
nightmare .goto( 'https://github.com' ) .wait(1000) .evaluate( function (){}) .end() |
(3)循环利用nightmare ,可以参照:nightmare async operations loops。
(4)如果想利用JQuery解析页面,得在evaluate方法前面利用inject()方法插入本地jquery.js文件,如:
1
|
.inject( 'js' , 'jquery.min.js' ) |
二、以音乐为例,模拟加载更多。
打开页面,我们可以发现页面底部有一个加载更多的按钮,而当我们等页面加载完成,加载更多这个按钮便会消失,因而我们可以利用nightmare的wait()方法,等待页面加载完毕后再去进行解析。
通过nightmare的API可以看到wait()方法如下,这说明wait会一直等待,直到返回ture,才会执行下面的操作(默认等待时间30s),因而结合YouTube页面加载的特性,可以考虑如果页面正在加载,我们返回false,如果页面底部还有加载更多,我们模拟点击按钮,同样返回false,直到检查到页面底部没有加载更多按钮时,我们才返回true,此时我们的页面就全部加载完成了,再去对页面进行解析就可以获取到加载更多以后的内容。
新建crawler.js文件,编辑如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
var Nightmare = require( 'nightmare' ); var nightmare = Nightmare({ show: true , pollInterval: 50 }); nightmare .goto( 'https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ' ) //.inject('js', 'jquery-2.1.1.min.js') .wait( function () { var loadMoreText = document.querySelector( '.load-more-text' ); if (loadMoreText == null ) return true ; if (/hid/.test(loadMoreText.classList.value)) return false ; document.querySelector( '.browse-items-load-more-button' ).click(); return false ; }) .evaluate( function () { //获取订阅号Id var $channelName = $( '#c4-primary-header-contents .branded-page-header-title a' ).attr( 'href' ); var $channelId = $channelName.match(/channel\/([a-zA-Z0-9_-]+)/); var category = []; $( '#browse-items-primary .branded-page-module-title' ).each( function (){ var $category = $( this ).find( 'a' ).first(); var item = { categoryName : $category.text().trim(), url : 'https://www.youtube.com' + $category.attr( 'href' ) }; //根据URL判断为订阅号还是视频分类 if (item.url.indexOf( 'list' )!==-1){ if (Array.isArray($channelId)){ item.channelId = $channelId[1]; } } else { var s = item.url.match(/channel\/([a-zA-Z0-9_-]+)/); if (Array.isArray(s)){ item.id = s[1]; } } //获取youtube某个订阅号下的视频分类 if (item.categoryName!== '' &&item.hasOwnProperty( 'channelId' )){ category.push(item); } }); return category; }) .end() .then( function (result) { console.log(result); console.log(result.length); }) . catch ( function (err) { console.log(err); }) |
运行代码后会发现这个订阅号下的所有视频分类都会显示出来,到这里我们的任务就完成了。
三、总结
通过这个简单的例子,我发现了nightmare的强悍之处,但是不适合用于处理大量页面,因为这样运行时间会很长,可以将其用于获取相关列表的url等信息,获取下来后到具体的页面可以采取其他模块,如node-crawler。采用笔记三的思路,我爬取YouTube时,先通过订阅号获取到每个订阅号下面的分类列表,再通过分类列表获取到视频列表,然后再到具体的视频。我通过测试5个订阅号,获取到视频列表大概花去5分钟的时间,视频数量大约为15000个,这个速度还可以。但是当我进入具体视频时,页面就变成了一万多个,解析一个差不多会花掉两秒的样子,这样的话速度太慢了。因而到第三层可以考虑采用其他模块解析,源码请点击。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库