9.3 PhantomJS
9.2节讲了直接从JavaScript中采集加载的数据的方法,本节进行讲解第二种方法,即直接从浏览器中提取渲染好的HTML文档。如果Ajax请求很多,有时请求参数还进行了加密,我们手动分析每一个Ajax请求,将成为一项繁重的工作,而且没有一定的JavaScript分析功底,很难做到。这个时候第二种方法的好处就体现出来了,直接提取浏览器渲染好的结果,不进行Ajax请求分析,PhantomJS就是这样的一个浏览器。
PhantomJS是一个基于WebKit的服务器端JavaScript API。它全面支持Web而无需浏览器支持,不仅运行快,原生支持各种Web标准:DOM处理、CSS选择器、JSON、Canvas,和SVG。PhantomJS可以用于页面自动化、网络监测、网页截屏,以及无界面测试等。PhantomJS可以看做一个没有界面的浏览器,它既有Firefox浏览器、google浏览器的功能,又因为没有界面而更加快速,占更小的内存,在爬虫开发中非常受欢迎。
9.3.1 安装PhantomJS
PhantomJS安装方法有两种,一种是下载源码之后自行编译,另一种是直接下载编译好的二进制文件,官方推荐直接使用编译好的二进制文件。安装下载地址为:http://phantomjs.org/download.html ,包括Windows、Mac OS、Linux版本,自行选择对应版本下载解压即可,建议为PhantomJS设置环境变量。在下载的安装包中,其中有一个example文件夹,里面有很多官方的例子可供学习和参考。
安装完成后在命令行中输入:phantomjs-v。如果正常显示版本号,则证明安装配置成功。图9-5为Windows下的显示结果。
图9-5 phantomJS版本
9.3.2 快速入门
配置完成PhantomJS,下面使用它输出“hello world”。新建一个JavaScript文件hello.js,代码内容为:
console.log('Hello, world!'); phantom.exit();
这时候在命令行中输入:
phantomjs hello.js
输出内容为:Hello,world!。代码中的第一句是在控制台输出“Hello,world!”,第二句是终止phantom的运行,不然程序会一直运行,不会停止。
通过上面的小例子我们已经了解了PhantomJS的基本操作,PhantomJS还有一些有趣而且强大功能。
1.页面加载
通过PhantomJS,一个网页可以被加载、分析和通过创建网页对象呈现。下面演示一个简单的页面加载的例子,访问我的博客园地址:http://www.cnblogs.com/qiyeboy/ ,并将当前页面进行截图保存。pageload.js代码如下:
var page = require('webpage').create(); page.open('http://www.cnblogs.com/qiyeboy/', function(status) { console.log("Status: " + status); if(status === "success") { page.render('qiye.png'); } phantom.exit(); });
在命令行中运行:
phantomjs pageload.js
输出内容为:Status:success,并在当前目录下生成对网页的截图qiye.png,如图9-6所示。
图9-6 qiye.png
代码解释:首先使用webpage模块创建一个page对象,然后通过page对象打开http://www.cnblogs.com/qiyeboy/ 网址,如果请求响应状态为success,则通过render方法将当前页面保存为qiye.png图片。
除了打开网页截图之外,还可以对网页的打开进行测速。下面的例子用来计算一个网页的加载速度,同时还用到了给JavaScript脚本传递参数的功能。loadspeed.js代码如下:
var page = require('webpage').create(), system = require('system'), t, address; if (system.args.length === 1) { console.log('Usage: loadspeed.js <some URL>'); phantom.exit(); } t = Date.now(); address = system.args[1]; page.open(address, function(status) { if (status !== 'success') { console.log('FAIL to load the address'); } else { t = Date.now() - t; console.log('Loading ' + system.args[1]); console.log('Loading time ' + t + ' msec'); } phantom.exit(); });
在命令行中输入:
phantomjs loadspeed.js http://www.cnblogs.com/qiyeboy/
输出结果为:
Loading http://www.cnblogs.com/qiyeboy/ Loading time 793 msec
代码解释:首先使用webpage模块创建一个page对象,使用system模块获取系统对象system,并声明了两个变量t和address,用来保存时间和传入参数。如果传入参数的长度等于1,说明要加载的地址没有传入,进行提示并退出phantom。为什么要等于1呢?因为phantomjs loadspeed.js第一个参数是loadspeed.js。接着获取当前的时间,然后打开网页,获取加载完成后的时间,进行相减即可。
2.代码评估
为了评估网页中的JavaScript代码,可以利用evaluate。这个执行是“沙盒式”的,它不会去执行网页外的JavaScript代码。evaluate方法可以返回一个对象,然而返回值仅限于对象,不能包含函数(或闭包)。比如我们可以使用evaluate方法获取http://www.cnblogs.com/qiyeboy/ 页面的标题,evaluate.js代码如下:
var url = 'http://www.cnblogs.com/qiyeboy/'; var page = require('webpage').create(); page.open(url, function(status) { var title = page.evaluate(function() { return document.title; }); console.log('Page title is ' + title); phantom.exit(); });
在命令行中输入:
phantomjs evaluate.js
输出结果为:
Page title is七夜的故事 - 博客园
任何来自于网页并且包括来自evaluate()内部代码的控制台信息,默认不会显示。要覆盖此行为,使用onConsoleMessage回调方法。将evaluate.js代码改动如下:
var url = 'http://www.cnblogs.com/qiyeboy/'; var page = require('webpage').create(); page.onConsoleMessage = function(msg) { console.log('Page title is ' + msg); }; page.open(url, function(status) { page.evaluate(function() { console.log(document.title); }); phantom.exit(); });
在命令行中输入:
phantomjs evaluate.js
输出结果为:
Page title is七夜的故事 - 博客园
9.3.3 屏幕捕获
上节简单讲解了如何将网页保存为一张图片,下面详细解释一下这个屏幕捕获的功能。由于PhantomJS使用的是WebKit内核,一个真正的布局和渲染引擎,它可以捕捉一个网页的屏幕截图。另外PhantomJS可以渲染网页上的元素,所以它不仅可以用于HTML和CSS的内容转换,还可以用于SVG和画布。PhantomJS不仅可以将网页保存为png格式,还可以保存为jpg、gif和pdf格式。下面将pageload.js代码进行改动,转成pdf格式,代码如下:
var page = require('webpage').create(); page.open('http://www.cnblogs.com/qiyeboy/', function(status) { console.log("Status: " + status); if(status === "success") { page.render('qiye.pdf'); } phantom.exit(); });
最后生成的pdf文件,效果如图9-7所示。
图9-7 qiye.pdf
PhantomJS不仅可以将页面转化为不同的文件格式,还可以对视图进行缩放和裁剪,主要用到page对象中两个非常重要的属性:viewportSize和clipRect。viewportSize是视区的大小,其作用可以看做是将打开的浏览器窗口进行缩放。clipRect是在这个视区中裁剪矩形的大小,需要四个参数,前两个是基准点,后两个参数是宽高。下面将pageload.js进行改动,代码如下:
var page = require('webpage').create(); page.viewportSize = { width: 1024, height: 768 }; page.clipRect = { top: 0, left: 0, width: 512, height: 256 }; page.open('http://www.cnblogs.com/qiyeboy/', function(status) { console.log("Status: " + status); if(status === "success") { page.render('qiye.png'); } phantom.exit(); });
效果如图9-8所示,只是截取出了顶端一角。
图9-8 网页裁剪
9.3.4 网络监控
因为PhantomJS允许检验网络流量,因此它适合分析网络行为和性能,实现对网络的监听。当向远程服务器发送请求时,可以使用onResourceRequested和onResourceReceived两个方法嗅探所有的资源请求和响应。示例netmonitor.js代码如下:
var url = 'http://www.cnblogs.com/qiyeboy/'; var page = require('webpage').create(); page.onResourceRequested = function(request) { console.log('Request ' + JSON.stringify(request, undefined, 4)); }; page.onResourceReceived = function(response) { console.log('Receive ' + JSON.stringify(response, undefined, 4)); }; page.open(url);
在命令行中输入:
phantomjs netmonitor.js
请求和响应的信息会以JSON的格式进行显示,效果如图9-9所示。
图9-9 网络监控
9.3.5 页面自动化
PhantomJS可以加载和处理一个网页,非常适用于自动化处理,PhantomJS中标准JavaScript的DOM操作和CSS选择器都是生效的。下面使用一个小例子讲解一下DOM操作,获取MTime时光网的影评信息,HTML标记位置如图9-10所示。
图9-10 评分和票房标记
示例代码如下:
var page = require('webpage').create(); console.log('The default user agent is ' + page.settings.userAgent); page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0'; page.open('http://movie.mtime.com/108737/', function(status) { if (status !== 'success') { console.log('Unable to access network'); } else { var ua = page.evaluate(function() { return document.getElementById('ratingRegion').textContent; }); console.log(ua); } phantom.exit(); });
输出结果如下:
The default user agent is Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/ 538.1(KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1 7.7总分:104,335人评分 4,299人想看音乐 画面 导演 故事 …票房:5.92亿元
代码解释:首先创建page对象,接着将默认的User-Agent进行了修改,打开指定网页,当加载完成之后,执行DOM操作,获取id为ratingRegion元素下的内容,并打印出来。
大家可以看一下默认UserAgent的内容,会发现里面包含了PhantomJS关键字,一些网站就是通过这个关键字来识别是否正在使用PhantomJS爬取数据。
在1.6版本之后PhantomJS允许添加外部的JS库,比如下面的例子添加了jQuery,然后执行了jQuery代码。
var page = require('webpage').create(); page.open('http://www.sample.com', function() { page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min .js", function() { page.evaluate(function() { $("button").click(); }); phantom.exit() }); });
9.3.6 常用模块和方法
上面的例子中我们用到了phantom、webpage和system模块,在这三个模块基础上再讲一个fs模块。
1.phantom
对于phantom,主要讲解其中的五个方法,如表9-2所示。
表9-2 phantom方法
2.webpage
对于webpage,主要说一下includeJs、open两个普通方法,onInitialized、onLoadFinished两个回调方法。
includeJs方法原型为includeJs(url,callback){void},功能是包含从指定的URL获取远程javaScript脚本,并执行回调方法。示例代码如下:
var webPage = require('webpage'); var page = webPage.create(); page.includeJs( // Include the https version, you can change this to http if you like. 'https:// ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js', function() { (page.evaluate(function() { // jQuery is loaded, now manipulate the DOM var $loginForm = $('form# login'); $loginForm.find('input[name="username"]').value('phantomjs'); $loginForm.find('input[name="password"]').value('c45p3r'); })) } );
open方法比较复杂,有四种函数重载方式,分别为open(url,callback){void}、open(url,method,callback){void}、open(url,method,data,callback){void}、open(url,settings,callback){void}。open(url,callback)方法之前已经用过,第二种和第三种方式类似,所以下面主要说一下后两种形式。
open(url,method,data,callback)中url为链接,method为GET或者POST请求,data为附加的数据,callback为回调函数。示例如下,用于发送一个POST请求。
var webPage = require('webpage'); var page = webPage.create(); var postBody = 'user=username&password=password'; page.open('http://www.google.com/', 'POST', postBody, function(status) { console.log('Status: ' + status); // Do other things here... });
open(url,settings,callback)中url为链接,setting为对请求头和内容的设置,callback为回调函数。示例如下:
var webPage = require('webpage'); var page = webPage.create(); var settings = { operation: "POST", encoding: "utf8", headers: { "Content-Type": "application/json" }, data: JSON.stringify({ some: "data", another: ["custom", "data"] }) }; page.open('http://your.custom.api', settings, function(status) { console.log('Status: ' + status); // Do other things here... });
onInitialized是回调方法,在webpage对象被创建之后,url被加载之前被调用,主要是用来操作一些全局变量。示例代码如下:
var webPage = require('webpage'); var page = webPage.create(); page.onInitialized = function() { page.evaluate(function() { document.addEventListener('DOMContentLoaded', function() { console.log('DOM content has loaded.'); }, false); }); };
onLoadFinished是回调方法,在页面加载完成之后调用,方法还有一个参数status。如果加载成功status为success,否则为fail。webpage中open方法就是用这个方法作为回调函数。示例代码如下:
var webPage = require('webpage'); var page = webPage.create(); page.onLoadFinished = function(status) { console.log('Status: ' + status); // Do other things here... };
3.system
system模块只有属性,没有方法。下面通过表9-3列举一下system的属性及其含义。
表9-3 system属性
4.fs
fs模块全称为File System,主要是对文件系统进行操作。该模块方法很多,这里主要讲解创建文件、判断文件是否存在、读写文件的方法,如表9-4所示。
表9-4 fs方法
以上介绍了一些常用模块和方法,如果大家想详细了解相关内容,可以去phantom官网(http://phantomjs.org/api/ )查看完整的API文档。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论