PhantomJS 基础及示例
腾讯云技术社区-掘金主页持续为大家呈现云计算技术文章,欢迎大家关注!
作者:link
概述
PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.(http://phantomjs.org/)
PhantomJS是一个无界面的webkit内核浏览器,你可以把它当作一个没有界面的Safari。
安装
目前PhantomJS的最新版本的2.0,官方文档中有提到说:如果在使用老版本时碰到一些难解的bug,可以升级到最新版试试。
windows
直接下载phantomjs-2.0.0-windows.zip,并解压,将bin文件夹中的可执行文件phantomjs.exe的路径添加到环境变量后(可能需要重启机器才能生效),就可以在命令行环境(cmd或cygwin)中使用phantomjs命令执行js文件了。
Linux
安装二进制文件包
可以在Bitbucket下载已经编译好的二进制文件安装包,不过目前Linux提供到PhantomJS 1.9.8的安装包,最新的PhantomJS 2.0还没有发布。
安装方式:
- 下载phantomjs-1.9.8-linux-x86_64.tar.bz2
- 进入安装目录,解压二进制文件
> cd /usr/local
> tar zxvf phantomjs-1.9.8-linux-x86_64.tar.bz2
- 创建软链接mysql指向解压出来的文件夹,或将解压出来的文件夹重命名为phantomjs:
> ln -sf phantomjs-1.9.8-linux-x86_64/bin/phantomjs phantomjs
编译源码的方式
由于WebKit模块中有数千个文件,因此由源码编译PhantomJS会花费很长的时间,文档上说,开四个并行的进程进行编译工作,需要超过30分钟的时间,因此官方文档推荐直接下载和安装二进制文件。
具体的安装方法,这里就不再赘述,大家可以到官方文档上查看。
是否安装成功
我们可以使用下面的命令来查看PhantomJS是否安装成功:
> phantomjs -v
命令运行phantomjs xxx.js即可执行一个PhantomJS程序。
webpage模块
webpage是PhantomJS的核心模块,你可以通过以下方式,获得一个webpage模块的实例:
var webPage = require("webpage"),
page = webPage.create();
open()
打开一个url链接,并加载对应的页面,一旦页面加载完成,就会触发回调,你也可以使用page.onLoadFinished
方法来监听页面是否加载完成。下面,我们来用open()方法打开腾讯课堂:
var page = require("webpage").create;
page.open("http://ke.qq.com", function(status) {
if(status !== "success") {
console.log("open fail!");
}
phantom.exit();
});
上面的代码中,open()方法接受了两个参数。第一个参数是要打开网页的url(要记得加协议头哦!)
,默认使用GET方法打开,第二个参数是回调参数,网页加载完成后该函数将会执行,它的参数status
表示网页是否打开成功,打开成功就是success
,否则就是fail
。要注意的是,只要收到服务器返回的结果,status
参数就是success
,即使服务器返回的是404或500错误。
我们也可以使用其他的http方法打开页面。
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...
});
上面的代码是官方文档的事例,使用POST方法向服务器发送数据。open方法的第二个参数用来指定HTTP方法,第三个参数用来指定该方法所要使用的数据。
从PhantomJS 1.9开始,我们还可以使用json对象来对http请求进行更详细的配置。
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...
});
evaluate()
在打开一个网页后,我们往往有对其进行操作的需求,例如模拟点击登陆按钮、获取某个DOM元素等等,也就是需要在页面中执行javascript代码,这时候我们就需要使用到evaluate()方法。
// 获取打开页面的title
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();
});
由于因为evaluate()方法相当于一个沙盒,在其中是无法访问evaluate()之外的变量的。那如何将我想要获取的dom元素的id传进evaluate呢?
从PhantomJS 1.6开始,我们可以将外部变量以如下的方式传给evaluate内部,需要注意的是,能传入evaluate方法内部的参数只能是简单的基本类型,例如数值、字符串、json对象等能被JSON序列化的类型,而无法接受更复杂的对象,它的返回值也同样如此。
page.open('https://item.taobao.com/item.htm?id=520115087331', function(status) {
var domId = "J_SellCounter"
var sellCounter = page.evaluate(function(id) {
return document.getElementById(id).innerText;
}, domId);
console.log(sellCounter);
phantom.exit();
});
由于open()方法打开的网页内部的console语句,和evaluate()方法中的console语句都不会执行,给我们开发调试带来了不便。这时可以采用onConsoleMessage回调函数,来打印出上面两种情况中的console语句中的信息:
var webPage = require('webpage');
var page = webPage.create();
page.onConsoleMessage = function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
其中msg是需要打印的信息,lineNum和sourceId是console.log在文件中的行号以及这个文件对应的标识id。
includeJs()
可以使用includeJs()方法加载外部脚本,例如jquery。
var webPage = require('webpage');
var page = webPage.create();
page.open('http://www.example.com', function(status) {
if(status !== "success") {
console.log("open fail!");
}
page.includeJs('http://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');
$('#loginBtn').click();
});
phantom.exit();
});
})
注意,由于includeJs
是异步加载脚本,所以phantom.exit()
需要放在page.includeJs()
的回调函数中,否则phantomjs进程会过早退出。
render()
render()可以将打开的网页截图并保存成本地图片,可以将指定的图片文件名作为参数传入,render方法可以根据文件名的后缀将图片保存成对应的格式。目前支持PNG
、GIF
、JPEG
、PDF
四种图片格式。
var webPage = require('webpage');
var page = webPage.create();
page.viewportSize = { width: 1920, height: 1080 };
page.open("http://www.google.com", function start(status) {
page.render('google_home.jpeg', {format: 'jpeg', quality: '100'});
phantom.exit();
});
该方法的第一个参数是保存的文件名,第二个可选参数是一个JSON对象,format指定图片格式,quality指定0-100区间内的图片质量,必须是整数。
onResourceRequested
当页面去请求一个资源时,会触发onResourceRequested()方法的回调函数。回调函数接受两个参数,第一个参数requestData
是这个HTTP请求的元数据对象,包括以下属性:
- id: 所请求资源的id号,这个应该是phantomjs给标识的。
- method: 所使用的HTTP方法(GET/POST/PUT/DELETE等)。
- url: 所请求资源的URL
- time: 包含请求该资源时间的一个Date对象。
- headers: 该请求的http请求头中的信息数组。
第二个参数networkRequest
包含以下方法:
- abort(): 终止当前的网络请求,这会导致调用onResourceError回调函数。
- changeUrl(newUrl):改变当前网络请求的URL。
- setHeader(key, value):设置HTTP头信息。
var webPage = require('webpage');
var page = webPage.create();
page.onResourceRequested = function(requestData, networkRequest) {
console.log('Request (#' + requestData.id + '): ' + JSON.stringify(requestData));
};
page.open("http://ke.qq.com", function(status) {
if(status) {
console.log("fail!");
}
phantom.exit();
});
onResourceReceived
onResourceReceived属性用于指定一个回调函数,当网页收到所请求的资源时,就会执行该回调函数。回调函数只有一个参数,就是所请求资源的服务器发来的HTTP response的元数据对象,包括以下字段。
- id:所请求的资源编号,此编号phantomjs标识。
- url:所请求的资源的URL
- time:包含HTTP回应时间的Date对象
- headers:响应的HTTP头信息数组
- bodySize:解压缩后的收到的内容大小
- contentType:接到的内容种类
- redirectURL:重定向URL(如果有的话)
- stage:对于多数据块的HTTP回应,头一个数据块为start,最后一个数据块为end。
- status:HTTP状态码,成功时为200。
- statusText:HTTP状态信息,比如OK。
需要注意的是,该方法收到的response对象是没有response.body的具体内容的。
可以利用正则表达式,来筛选出我们想要操作的一些响应资源。比如我想从淘宝教育的课程详情页跳转到购买页(在淘宝网中),可以从淘宝同学请求的资源url中筛选出带淘宝网商品详情页的商品id,然后用这个淘宝网商品id拼接成一个淘宝网的商品详情页url,再次使用open()方法打开这个url,就可以跳转到该课程的购买页中。
var page = require('webpage').create(),
url1 = "http://i.xue.taobao.com/detail.htm?courseId=32679",
url2 = "https://item.taobao.com/item.htm?id=",
itemId = 0,
mItem = "",
siteType = "taobao";
page.onConsoleMessage = function(msg) {
console.log('console: ' + msg);
};
page.onResourceReceived = function(response) {
/*if(mItem = response.url.match(/^http\:\/\/(?:.*)[?|&]item=(\d*)/)) {
itemId = mItem[1];
console.log(itemId);
phantom.exit();
}*/
// 获取课程对应的淘宝网商品id
if(mItem = response.url.match(/itemId=(\d*)/)) {
itemId = parseInt(mItem[1]);
}
}
page.open(url1, function(status) {
if(status !== "success") {
console.log("tongxue fail!");
phantom.exit();
}
page.render("tongxue.png");
// 打开课程对应的淘宝商品详情页。
page.open(url2 + itemId, function(status) {
if(status !== "success") {
console.log("tongxue fail!");
phantom.exit();
}
// 由于页面中的资源是动态加载的,需要setTimeout 10s 等待资源加载完,再操作页面。
setTimeout(function() {
var apply = page.evaluate(function() {
// 获取课程交易量
return document.getElementById("J_SellCounter").innerText;
//return document.getElementById("bd").innerHTML;
});
console.log("apply:", apply);
//fs.write("body.html", apply, "w");
phantom.exit();
}, 10000);
});
});
小栗子
动态获取淘宝商品详情页的商品交易量
相信大家都知道爬虫的基本方式无非是抓取页面中的url,然后分析;但是页面中的url也些是静态的,有些事通过js动态生成的,故爬虫也分抓静及抓动之分。
因为淘宝商品详情页的交易量是异步拉取的,在异步数据还没有返回时,页面上交易量那一栏只是一个无意义的“-”,如图:
当异步数据返回后,才会显示出真正的交易量:
因此,
var webPage = require('webpage');
var page = webPage.create();
var pageTb = webPage.create();
var tbUrl = "https://item.taobao.com/item.htm?id=520115087331";
page.settings.userAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36";
pageTb.open(tbUrl, function(status) {
// 由于是拉取异步数据,我们打开页面后,等待12s再去操作dom,获取交易量
setTimeout(function() {
var result = pageTb.evaluate(function() {
return document.getElementById("J_SellCounter").innerText;
});
console.log(result);
//生成当前页面截图
pageTb.render("xuqintb2.png");
phantom.exit();
}, 12000);
});
win7上执行命令:
$ phantomjs.exe --ssl-protocol=any xuqinTb.js
1379
win7上得到了交易量(由于是打开https协议头的网页,所以执行js文件时,需要添加"--ssl-protocol=any"参数)
PhantomJS不能做什么
- PhantomJS是一个阉割版的webkit,不支持flash、webGL、video/audio、css 3-d,phontomjs不想背负操作系统强相关的特性,跨平台比较困难。
- 如果使用Page模块的onResourceReceived()方法监听页面收到的请求资源,是无法得到该资源的response.body的,这也是目前PhantomJS最受开发者吐槽的点之一。
相关推荐
腾讯云上PhantomJS用法示例
腾讯云上Selenium用法示例
包学会之浅入浅出Vue.js:开学篇
此文已由作者授权腾讯云技术社区发布,转载请注明文章出处
原文链接:https://www.qcloud.com/community/article/743451001489391682
获取更多腾讯海量技术实践干货,欢迎大家前往腾讯云技术社区
PhantomJS 基础及示例的更多相关文章
- PhantomJS 基础及示例 (转)
概述 PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast and native support f ...
- canvas基础动画示例
canvas基础动画示例 本文主要用最简单的例子,展示canvas动画效果是如何实现的 动画效果,是一个球绕着一点旋转 const canvas = document.getElementById(' ...
- docker-compose 构建mongodb并导入基础数据示例
使用docker-compose构建mongodb服务并导入基础数据示例. 1.文件目录结构 ——mongo/ |——docker-compose.yml |——mongo-Dockerfile |— ...
- Spring温习(1)--最基础的示例
Spring温习(1)--最基础的示例 博客分类: 框架-Spring专栏 SpringXMLBeanWebDAO 从现在开始,我将从Spring为起点,逐步复习几大框架各方面的知识,以便今后查看使用 ...
- java 网络编程(三)---TCP的基础级示例
下面是TCP java网络编程的基础示例: tcp传输:客户端建立过程的思路:1.创建TCP客户端的Socket服务,使用的是socket对象,建议在创建的过程中,就明确了目的地和要连接的主机2.如果 ...
- asp.net core系列 59 Ocelot 构建基础项目示例
一.入门概述 从这篇开始探讨Ocelot,Ocelot是一个.NET API网关,仅适用于.NET Core,用于.NET面向微服务/服务的架构中.当客户端(web站点.ios. app 等)访问we ...
- Spring Boot 1.5.x 基础学习示例
一.为啥要学Spring Boot? 今年从原来.Net Team“被”转到了Java Team开始了微服务开发的工作,接触了Spring Boot这个新瓶装旧酒的技术,也初步了解了微服务架构.Spr ...
- Confluence 6 配置服务器基础地址示例
如果 Confluence 的安装是没有安装在非根目录路径(这个是上下文路径),然后服务器基础 URL 地址应该包括上下文地址.例如,你的 Confluence 正在运行在下面的地址: http:// ...
- KVM虚拟化原理与基础应用示例
一.KVM简介 Kernel-based Virtual Machine的简称,是一个开源的系统虚拟化模块,自Linux 2.6.20之后集成在Linux的各个主要发行版本中.它使用Linux自身的调 ...
随机推荐
- java.sql.Date和java.util.Date的不同和相互转换方式
一:前言 这是我在新的公司写的第一份博客吧,来了又一个星期了吧,但是在来的那几天我真的很迷茫的感觉这里是很不适合我的样子,而且我又是来实习的,我很不愿意啊,自己做的又是java web,最原始的ser ...
- Java输入输出流备忘
重要博客: http://blog.csdn.net/hguisu/article/details/7418161 File dir = new File("\\root"); ...
- HDOJ 3501 Calculation 2
题目链接 分析: 要求的是小于$n$的和$n$不互质的数字之和...那么我们先求出和$n$互质的数字之和,然后减一减就好了... $\sum _{i=1}^{n} i[gcd(i,n)==1]=\le ...
- The service base of EF I am using
using CapMon.Data; using System; using System.Collections.Generic; using System.Linq; using System.T ...
- bzoj 2190 线性生成欧拉函数表
首先我们知道,正方形内个是对称的,关于y=x对称,所以只需要算出来一半的人数 然后乘2+1就行了,+1是(1,1)这个点 开始我先想的递推 那么我们对于一半的三角形,一列一列的看,假设已经求好了第I- ...
- MySQL中EXISTS的用法
比如在Northwind数据库中有一个查询为 SELECT c.CustomerId,CompanyName FROM Customers c WHERE EXISTS( SELECT OrderID ...
- IOS工程自动打包并发布脚本实现
http://blog.csdn.net/ccf0703/article/details/8588667 文章首发地址:http://webfrogs.me/2013/02/18/ios-automa ...
- Linux 下编译安装OpenCV【转】
转自:http://www.cnblogs.com/emouse/archive/2013/02/22/2922940.html OpenCV 2.2以后版本需要使用Cmake生成makefile文件 ...
- boost::function的简单实现
前言 boost::function和boost:bind是一对强大的利器.相信用过的童鞋多少有些体会. 虽然平时在用boost::function,但是用的时候心中总会一些不安,因为不知道它是怎么实 ...
- 学习apache commons lang3的源代码 (1):前言和R
本系列主要是针对lang3的3.7版本的源代码进行学习,并适当举例.一共大概150多个java文件,争取30天内学习完毕. 26个英文字母 争取每天学习1个字母开头的类们. 今天,就学习R开头的吧. ...