目标:

  解决页面加载更多问题。笔记三中,我们只爬取到网页的部分信息,而点击加载更多后的页面内容是没有提取到的。开始我的想法是找到加载更多的数据接口(可参照: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 不会执行任何操作。例如执行下面这段代码,在打开浏览窗口之后不会有任何操作结果。

nightmare
.goto('https://github.com')
.wait(1000)
.evaluate(function(){})
.end()

  

  (3)循环利用nightmare ,可以参照:nightmare async operations loops。

  (4)如果想利用JQuery解析页面,得在evaluate方法前面利用inject()方法插入本地jquery.js文件,如:

.inject('js', 'jquery.min.js')

二、以音乐为例,模拟加载更多。

  打开页面,我们可以发现页面底部有一个加载更多的按钮,而当我们等页面加载完成,加载更多这个按钮便会消失,因而我们可以利用nightmare的wait()方法,等待页面加载完毕后再去进行解析。

  通过nightmare的API可以看到wait()方法如下,这说明wait会一直等待,直到返回ture,才会执行下面的操作(默认等待时间30s),因而结合YouTube页面加载的特性,可以考虑如果页面正在加载,我们返回false,如果页面底部还有加载更多,我们模拟点击按钮,同样返回false,直到检查到页面底部没有加载更多按钮时,我们才返回true,此时我们的页面就全部加载完成了,再去对页面进行解析就可以获取到加载更多以后的内容。

  新建crawler.js文件,编辑如下代码:

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个,这个速度还可以。但是当我进入具体视频时,页面就变成了一万多个,解析一个差不多会花掉两秒的样子,这样的话速度太慢了。因而到第三层可以考虑采用其他模块解析,源码请点击。

nodejs爬虫笔记(四)---利用nightmare解决加载更多问题的更多相关文章

  1. nodejs爬虫笔记(五)---利用nightmare模拟点击下一页

    目标 以腾讯滚动新闻为例,利用nightmare模拟点击下一页,爬取所有页面的信息.首先得感谢node社区godghdai的帮助,开始接触不太熟悉nightmare,感觉很高大上,自己写代码的时候问题 ...

  2. nodejs爬虫笔记(三)---爬取YouTube网站上的视频信息

    思路:通过笔记(二)中代理的设置,已经可以对YouTube的信息进行爬取了,这几天想着爬取网站下的视频信息.通过分析YouTube,发现可以从订阅号入手,先选择几个订阅号,然后爬取订阅号里面的视频分类 ...

  3. nodejs爬虫笔记(二)---代理设置

    node爬虫代理设置 最近想爬取YouTube上面的视频信息,利用nodejs爬虫笔记(一)的方法,代码和错误如下 var request = require('request'); var chee ...

  4. .Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题)

    在.Net Framework时代,生成类库只需将类库项目编译好,然后拷贝到其他项目,即可引用或动态加载,相对来说,比较简单.但到了.Net Core时代,动态加载第三方类库,则稍微麻烦一些. 一.类 ...

  5. 爬虫(七)图片懒加载技术、selenium和PhantomJS

    动态数据加载处理 一.图片懒加载 什么是图片懒加载? 案例分析:抓取站长素材http://sc.chinaz.com/中的图片数据 #!/usr/bin/env python # -*- coding ...

  6. C# 利用反射动态加载dll

    笔者遇到的一个问题,dll文件在客户端可以加载成功,在web端引用程序报错.解决方法:利用反射动态加载dll 头部引用加: using System.Reflection; 主要代码: Assembl ...

  7. 微信小程序(五) 利用模板动态加载数据

    利用模板动态加载数据,其实是对上一节静态数据替换成动态数据:

  8. 图片_ _Android有效解决加载大图片时内存溢出的问题 2

    Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ...

  9. ionic上拉加载更多解决方法

    第一步: $scope.hasmore = true;//是否允许上拉加载 $scope.num = 8;//显示条数 第二步://查询显示内容,查出所有的 $scope.Group = functi ...

随机推荐

  1. Undefined symbols for architecture arm64(其cpu架构)

    "_OBJC_CLASS_$_XXX",referenced from: 之类的问题,很多人看到这些很头疼,不知道从何下手去修改.以下是我总结的相关修改办法.如下图 这是一个很明显 ...

  2. 使用xUnit为.net core程序进行单元测试(中)

    第一部分: http://www.cnblogs.com/cgzl/p/8283610.html 下面有一点点内容是重叠的.... String Assert 测试string是否相等: [Fact] ...

  3. api接口写好了?想过(Accept,Content-Type)?返回类型json|xml?

    api接口写好了?想过(Accept,Content-Type)?返回类型json|xml? 起因: - A,B. A调用B提供的api接口. - A:为毛你的接口返回的是xml格式的(浏览器访问)? ...

  4. hibernate使用注解配置索引

    添加普通索引 @Table(name="t_token", indexes={@Index(name="token_strIndex", columnList= ...

  5. 大话命令之--ss

    大话命令之-ss ss是Socket Statistics的缩写.顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容. 优势: (1)显示更多更详细的有关TCP和 ...

  6. [PHP] PHP与Apache的模块配合说明

    1.当PHP需要在Apache服务器下运行时 一般来说,它可以mod_php5模块的形式集成, 此时mod_php5模块的作用是接收Apache传递过来的PHP文件请求,并处理这些请求, 然后将处理后 ...

  7. JavaScript基础知识(数据类型及转换、运算符)

    9.数据类型 概念:表示当前存储的数据的分类(表示数字 - 整数和小数) u  原始类型(原始值) -----[typeof运算符:判断变量的原始类型] *number(数字):表示数字        ...

  8. 解决:java.io.IOException: No FileSystem for scheme: hdfs

    解决:java.io.IOException: No FileSystem for scheme: hdfs 开发项目初期,写完代码开始放到服务器上开始测试的时候,报出这样的一个错,不知道怎么处理了, ...

  9. XML(一)XML大揭秘

    前言 每天都要学习很多新的知识,比你厉害的程序员比你还努力,那你混的下这口饭吗?所以不抱怨,坚持!接下来给大家分享的是xml.可能很多做开发的都遇到过xml, 比如maven,各种框架的配置文件都有, ...

  10. LibreOJ β Round #2 F. 数学上来先打表

    传送门 题解 做法与题解基本无异,不过他说用vector我觉得用链表来得更好一些. #include<queue> #include<ctime> #include<bi ...