前言

大家有没有发现之前我们写的爬虫都有一个共性,就是只能爬取单纯的html代码,如果页面是JS渲染的该怎么办呢?如果我们单纯去分析一个个后台的请求,手动去摸索JS渲染的到的一些结果,那简直没天理了。所以,我们需要有一些好用的工具来帮助我们像浏览器一样渲染JS处理的页面。

其中有一个比较常用的工具,那就是PhantomJS

Full web stack No browser required
PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fastandnative support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.

PhantomJS是一个无界面的,可脚本编程的WebKit浏览器引擎。它原生支持多种web 标准:DOM 操作,CSS选择器,JSON,Canvas 以及SVG。

好,接下来我们就一起来了解一下这个神奇好用的库的用法吧。

安装

PhantomJS安装方法有两种,一种是下载源码之后自己来编译,另一种是直接下载编译好的二进制文件。然而自己编译需要的时间太长,而且需要挺多的磁盘空间。官方推荐直接下载二进制文件然后安装。

大家可以依照自己的开发平台选择不同的包进行下载
下载地址
当然如果你不嫌麻烦,可以选择
下载源码
然后自己编译。

目前(2016/3/21)最新发行版本是 v2.1,安装完成之后命令行输入

phantomjs -v

如果正常显示版本号,那么证明安装成功了。如果提示错误,那么请重新安装。
本文介绍大部分内容来自于官方文档,博主对其进行了整理,学习更多请参考
官方文档

安装PhantomJS(windows环境安装)

  1. 下载的文件名:phantomjs-2.1.1-windows.zip,直接解压出来,解压后的文件复制到你电脑的任意盘(我是放在D盘),建议别放c盘,然后进入解压目录,phantomjs-2.1.1\bin下有个phantomjs.exe,双击就可以进入命令行了;
  2. 添加环境变量,在cmd中就可使用phantomjs命令了,添加完之后就是以下界面:

快速开始

1.第一个程序

第一个程序当然是Hello World,新建一个 js 文件。命名为 helloworld.js

console.log('Hello, world!');
phantom.exit();

命令行输入

phantomjs helloworld.js

程序输出了 Hello,world!程序第二句话终止了 phantom 的执行。

注意:phantom.exit();这句话非常重要,否则程序将永远不会终止。

2.页面加载

可以利用 phantom 来实现页面的加载,下面的例子实现了页面的加载并将页面保存为一张图片。

var page = require('webpage').create();
page.open('http://cuiqingcai.com', function (status) {
    console.log("Status: " + status);
    if (status === "success") {
        page.render('example.png');
    }
    phantom.exit();
});

首先创建了一个webpage对象,然后加载本站点主页,判断响应状态,如果成功,那么保存截图为 example.png

以上代码命名为 pageload.js,命令行

phantomjs pageload.js

发现执行成功,然后目录下多了一张图片,example.png

因为这个 render 方法,phantom 经常会用到网页截图的功能。

3.测试页面加载速度

下面这个例子计算了一个页面的加载速度,同时还用到了命令行传参的特性。新建文件保存为 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://cuiqingcai.com

运行结果

Loading http://cuiqingcai.com
Loading time 11678 msec

这个时间包括JS渲染的时间,当然和网速也有关。

4.代码评估

To evaluate JavaScript code in the context of the web page, use evaluate()function. The execution is “sandboxed”, there is no way for the code to access any JavaScript objects and variables outside its own page context. An object can be returned from evaluate(), however it is limited to simple objects and can’t contain functions or closures.

利用 evaluate 方法我们可以获取网页的源代码。这个执行是“沙盒式”的,它不会去执行网页外的 JavaScript 代码。evalute 方法可以返回一个对象,然而返回值仅限于对象,不能包含函数(或闭包)

var url = 'http://www.baidu.com';
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();
});

以上代码获取了百度的网站标题。

Page title is 百度一下,你就知道

任何来自于网页并且包括来自 evaluate() 内部代码的控制台信息,默认不会显示。

需要重写这个行为,使用 onConsoleMessage 回调函数,示例可以改写成

var url = 'http://www.baidu.com';
var page = require('webpage').create();
page.onConsoleMessage = function (msg) {
    console.log(msg);
};
page.open(url, function (status) {
    page.evaluate(function () {
        console.log(document.title);
    });
    phantom.exit();
});

这样的话,如果你用浏览器打开百度首页,打开调试工具的console,可以看到控制台输出信息。

重写了 onConsoleMessage 方法之后,可以发现控制台输出的结果和我们需要输出的标题都打印出来了。

一张网页,要经历怎样的过程,才能抵达用户面前?
一位新人,要经历怎样的成长,才能站在技术之巅?
探寻这里的秘密;
体验这里的挑战;
成为这里的主人;
加入百度,加入网页搜索,你,可以影响世界。

请将简历发送至 %c ps_recruiter@baidu.com( 邮件标题请以“姓名-应聘XX职位-来自console”命名) color:red
职位介绍:http://dwz.cn/hr2013
百度一下,你就知道

啊,我没有在为百度打广告!

5.屏幕捕获

Since PhantomJS is using WebKit, a real layout and rendering engine, it can capture a web page as a screenshot. Because PhantomJS can render anything on the web page, it can be used to convert contents not only in HTML and CSS, but also SVG and Canvas.

因为 PhantomJS 使用了 WebKit内核,是一个真正的布局和渲染引擎,它可以像屏幕截图一样捕获一个web界面。
因为它可以渲染网页中的任何元素,所以它不仅用到HTML,CSS的内容转化,还用在SVG,Canvas。可见其功能是相当强大的。

下面的例子就捕获了github网页的截图。上文有类似内容,不再演示。

var page = require('webpage').create();
page.open('http://github.com/', function() {
  page.render('github.png');
  phantom.exit();
});

除了 png 格式的转换,PhantomJS还支持 jpg,gif,pdf等格式。
测试样例

其中最重要的方法便是 viewportSize 和 clipRect属性。

  • viewportSize 是视区的大小,你可以理解为你打开了一个浏览器,然后把浏览器窗口拖到了多大。
  • clipRect 是裁切矩形的大小,需要四个参数,前两个是基准点,后两个参数是宽高。

通过下面的小例子感受一下。

var page = require('webpage').create();
//viewportSize being the actual size of the headless browser
page.viewportSize = { width: 1024, height: 768 };
//the clipRect is the portion of the page you are taking a screenshot of
page.clipRect = { top: 0, left: 0, width: 1024, height: 768 };
//the rest of the code is the same as the previous example
page.open('http://cuiqingcai.com/', function() {
  page.render('germy.png');
  phantom.exit();
});

运行结果

就相当于把浏览器窗口拖到了 1024×768 大小,然后从左上角裁切出了 1024×768 的页面。

6.网络监听

Because PhantomJS permits the inspection of network traffic, it is suitable to build various analysis on the network behavior and performance.

因为 PhantomJS 有网络通信的检查功能,它也很适合用来做网络行为的分析。

When a page requests a resource from a remote server, both the request and the response can be tracked via onResourceRequested and onResourceReceived callback.

当接受到请求时,可以通过改写onResourceRequestedonResourceReceived回调函数来实现接收到资源请求和资源接受完毕的监听。例如

var url = 'http://www.cuiqingcai.com';
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);
phantom.exit();

运行结果会打印出所有资源的请求和接收状态,以JSON格式输出。

7.页面自动化处理

Because PhantomJS can load and manipulate a web page, it is perfect to carry out various page automations.

因为 PhantomJS 可以加载和操作一个web页面,所以用来自动化处理也是非常适合的。

DOM操作

Since the script is executed as if it is running on a web browser, standard DOM scripting and CSS selectors work just fine.

脚本都是像在浏览器中运行的,所以标准的 JavaScript 的 DOM 操作和 CSS 选择器也是生效的。

例如下面的例子就修改了 User-Agent,然后还返回了页面中某元素的内容。

var page = require('webpage').create();
console.log('The default user agent is ' + page.settings.userAgent);
page.settings.userAgent = 'SpecialAgent';
page.open('http://www.httpuseragent.org', function(status) {
  if (status !== 'success') {
    console.log('Unable to access network');
  } else {
    var ua = page.evaluate(function() {
      return document.getElementById('myagent').textContent;
    });
    console.log(ua);
  }
  phantom.exit();
});

运行结果

The default user agent is Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.0 Safari/538.1
Your Http User Agent string is: SpecialAgent

首先打印出了默认的 User-Agent,然后通过修改它,请求验证 User-Agent 的一个站点,通过选择器得到了修改后的 User-Agent。

使用附加库

在1.6版本之后允许添加外部的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()
  });
});

引用了 jQuery 之后,我们便可以在下面写一些 jQuery 代码了。

8.Webpage对象

在前面我们介绍了 webpage 对象的几个方法和属性,其实它本身还有其它很多的属性。具体的内容可以参考
Webpage
Webpage用例

// The purpose of this is to show how and when events fire, considering 5 steps
// happening as follows:
//
//      1. Load URL
//      2. Load same URL, but adding an internal FRAGMENT to it
//      3. Click on an internal Link, that points to another internal FRAGMENT
//      4. Click on an external Link, that will send the page somewhere else
//      5. Close page
//
// Take particular care when going through the output, to understand when
// things happen (and in which order). Particularly, notice what DOESN'T
// happen during step 3.
//
// If invoked with "-v" it will print out the Page Resources as they are
// Requested and Received.
//
// NOTE.1: The "onConsoleMessage/onAlert/onPrompt/onConfirm" events are
// registered but not used here. This is left for you to have fun with.
// NOTE.2: This script is not here to teach you ANY JavaScript. It's aweful!
// NOTE.3: Main audience for this are people new to PhantomJS.

"use strict";
var sys = require("system"),
    page = require("webpage").create(),
    logResources = false,
    step1url = "http://en.wikipedia.org/wiki/DOM_events",
    step2url = "http://en.wikipedia.org/wiki/DOM_events#Event_flow";

if (sys.args.length > 1 && sys.args[1] === "-v") {
    logResources = true;
}

function printArgs() {
    var i, ilen;
    for (i = 0, ilen = arguments.length; i < ilen; ++i) {
        console.log("    arguments[" + i + "] = " + JSON.stringify(arguments[i]));
    }
    console.log("");
}

////////////////////////////////////////////////////////////////////////////////

page.onInitialized = function() {
    console.log("page.onInitialized");
    printArgs.apply(this, arguments);
};
page.onLoadStarted = function() {
    console.log("page.onLoadStarted");
    printArgs.apply(this, arguments);
};
page.onLoadFinished = function() {
    console.log("page.onLoadFinished");
    printArgs.apply(this, arguments);
};
page.onUrlChanged = function() {
    console.log("page.onUrlChanged");
    printArgs.apply(this, arguments);
};
page.onNavigationRequested = function() {
    console.log("page.onNavigationRequested");
    printArgs.apply(this, arguments);
};
page.onRepaintRequested = function() {
    console.log("page.onRepaintRequested");
    printArgs.apply(this, arguments);
};

if (logResources === true) {
    page.onResourceRequested = function() {
        console.log("page.onResourceRequested");
        printArgs.apply(this, arguments);
    };
    page.onResourceReceived = function() {
        console.log("page.onResourceReceived");
        printArgs.apply(this, arguments);
    };
}

page.onClosing = function() {
    console.log("page.onClosing");
    printArgs.apply(this, arguments);
};

// window.console.log(msg);
page.onConsoleMessage = function() {
    console.log("page.onConsoleMessage");
    printArgs.apply(this, arguments);
};

// window.alert(msg);
page.onAlert = function() {
    console.log("page.onAlert");
    printArgs.apply(this, arguments);
};
// var confirmed = window.confirm(msg);
page.onConfirm = function() {
    console.log("page.onConfirm");
    printArgs.apply(this, arguments);
};
// var user_value = window.prompt(msg, default_value);
page.onPrompt = function() {
    console.log("page.onPrompt");
    printArgs.apply(this, arguments);
};

////////////////////////////////////////////////////////////////////////////////

setTimeout(function() {
    console.log("");
    console.log("### STEP 1: Load '" + step1url + "'");
    page.open(step1url);
}, 0);

setTimeout(function() {
    console.log("");
    console.log("### STEP 2: Load '" + step2url + "' (load same URL plus FRAGMENT)");
    page.open(step2url);
}, 5000);

setTimeout(function() {
    console.log("");
    console.log("### STEP 3: Click on page internal link (aka FRAGMENT)");
    page.evaluate(function() {
        var ev = document.createEvent("MouseEvents");
        ev.initEvent("click", true, true);
        document.querySelector("a[href='#Event_object']").dispatchEvent(ev);
    });
}, 10000);

setTimeout(function() {
    console.log("");
    console.log("### STEP 4: Click on page external link");
    page.evaluate(function() {
        var ev = document.createEvent("MouseEvents");
        ev.initEvent("click", true, true);
        document.querySelector("a[title='JavaScript']").dispatchEvent(ev);
    });
}, 15000);

setTimeout(function() {
    console.log("");
    console.log("### STEP 5: Close page and shutdown (with a delay)");
    page.close();
    setTimeout(function(){
        phantom.exit();
    }, 100);
}, 20000);

里面介绍了 webpage的所有属性,方法,回调。

9.命令行

Command-line Options

PhantomJS提供的命令行选项有:

–help or -h lists all possible command-line options. Halts immediately, will not run a script passed as argument. [帮助列表]
–version or -v prints out the version of PhantomJS. Halts immediately, will not run a script passed as argument. [查看版本]
–cookies-file=/path/to/cookies.txt specifies the file name to store the persistent Cookies. [指定存放cookies的路径]
–disk-cache=[true|false] enables disk cache (at desktop services cache storage location, default is false). Also accepted: [yes|no]. [硬盘缓存开关,默认为关]
–ignore-ssl-errors=[true|false] ignores SSL errors, such as expired or self-signed certificate errors (default is false). Also accepted: [yes|no]. [忽略ssl错误,默认不忽略]
–load-images=[true|false] load all inlined images (default is true). Also accepted: [yes|no]. [加载图片,默认为加载]
–local-storage-path=/some/path path to save LocalStorage content and WebSQL content. [本地存储路径,如本地文件和SQL文件等]
–local-storage-quota=number maximum size to allow for data. [本地文件最大大小]
–local-to-remote-url-access=[true|false] allows local content to access remote URL (default is false). Also accepted: [yes|no]. [是否允许远程加载文件,默认不允许]
–max-disk-cache-size=size limits the size of disk cache (in KB). [最大缓存空间]
–output-encoding=encoding sets the encoding used for terminal output (default is utf8). [默认输出编码,默认utf8]
–remote-debugger-port starts the script in a debug harness and listens on the specified port [远程调试端口]
–remote-debugger-autorun runs the script in the debugger immediately: ‘yes’ or ‘no’ (default) [在调试环境下是否立即执行脚本,默认否]
–proxy=address:port specifies the proxy server to use (e.g. –proxy=192.168.1.42:8080). [代理]
–proxy-type=[http|socks5|none] specifies the type of the proxy server (default is http). [代理类型,默认http]
–proxy-auth specifies the authentication information for the proxy, e.g. –proxy-auth=username:password). [代理认证]
–script-encoding=encoding sets the encoding used for the starting script (default is utf8). [脚本编码,默认utf8]
–ssl-protocol=[sslv3|sslv2|tlsv1|any’] sets the SSL protocol for secure connections (default is SSLv3). [SSL协议,默认SSLv3]
–ssl-certificates-path=<val> Sets the location for custom CA certificates (if none set, uses system default). [SSL证书路径,默认系统默认路径]
–web-security=[true|false] enables web security and forbids cross-domain XHR (default is true). Also accepted: [yes|no]. [是否开启安全保护和禁止异站Ajax,默认开启保护]
–webdriver starts in ‘Remote WebDriver mode’ (embedded GhostDriver): ‘[[:]]’ (default ‘127.0.0.1:8910’) [以远程WebDriver模式启动]
–webdriver-selenium-grid-hub URL to the Selenium Grid HUB: ‘URLTOHUB’ (default ‘none’) (NOTE: works only together with ‘–webdriver’) [Selenium接口]
–config=/path/to/config.json can utilize a JavaScript Object Notation (JSON) configuration file instead of passing in multiple command-line optionss [所有的命令行配置从config.json中读取]

注:JSON文件配置格式

{
  /* Same as: --ignore-ssl-errors=true */
  "ignoreSslErrors": true,

  /* Same as: --max-disk-cache-size=1000 */
  "maxDiskCacheSize": 1000,

  /* Same as: --output-encoding=utf8 */
  "outputEncoding": "utf8"

  /* etc. */
}

There are some keys that do not translate directly:

 * --disk-cache => diskCacheEnabled
 * --load-images => autoLoadImages
 * --local-storage-path => offlineStoragePath
 * --local-storage-quota => offlineStorageDefaultQuota
 * --local-to-remote-url-access => localToRemoteUrlAccessEnabled
 * --web-security => webSecurityEnabled

以上是命令行的基本配置

10.实例

在此提供官方文档实例,多对照实例练习,使用起来会更得心应手。
官方实例

文章来源:http://cuiqingcai.com/2577.html

Python爬虫利器四之PhantomJS的用法的更多相关文章

  1. Python爬虫进阶四之PySpider的用法

    审时度势 PySpider 是一个我个人认为非常方便并且功能强大的爬虫框架,支持多线程爬取.JS动态解析,提供了可操作界面.出错重试.定时爬取等等的功能,使用非常人性化. 本篇内容通过跟我做一个好玩的 ...

  2. Python爬虫利器六之PyQuery的用法

    前言 你是否觉得 XPath 的用法多少有点晦涩难记呢? 你是否觉得 BeautifulSoup 的语法多少有些悭吝难懂呢? 你是否甚至还在苦苦研究正则表达式却因为少些了一个点而抓狂呢? 你是否已经有 ...

  3. Python爬虫利器五之Selenium的用法

    1.简介 Selenium 是什么?一句话,自动化测试工具.它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果你在这些浏览器里面安装一个 Selenium 的 ...

  4. (转)Python爬虫利器一之Requests库的用法

    官方文档 以下内容大多来自于官方文档,本文进行了一些修改和总结.要了解更多可以参考 官方文档 安装 利用 pip 安装 $ pip install requests 或者利用 easy_install ...

  5. Python爬虫利器二之Beautiful Soup的用法

    上一节我们介绍了正则表达式,它的内容其实还是蛮多的,如果一个正则匹配稍有差池,那可能程序就处在永久的循环之中,而且有的小伙伴们也对写正则表达式的写法用得不熟练,没关系,我们还有一个更强大的工具,叫Be ...

  6. Python爬虫利器一之Requests库的用法

    前言 之前我们用了 urllib 库,这个作为入门的工具还是不错的,对了解一些爬虫的基本理念,掌握爬虫爬取的流程有所帮助.入门之后,我们就需要学习一些更加高级的内容和工具来方便我们的爬取.那么这一节来 ...

  7. Python爬虫入门四之Urllib库的高级用法

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

  8. 转 Python爬虫入门四之Urllib库的高级用法

    静觅 » Python爬虫入门四之Urllib库的高级用法 1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我 ...

  9. python爬虫利器Selenium使用详解

    简介: 用pyhon爬取动态页面时普通的urllib2无法实现,例如下面的京东首页,随着滚动条的下拉会加载新的内容,而urllib2就无法抓取这些内容,此时就需要今天的主角selenium. Sele ...

随机推荐

  1. CUDA跟OpenCV的混合编程,注意OpenCV需要重新编译

    1.注意事项 编译的办法参见: http://blog.csdn.net/wangyaninglm/article/details/39997113   以下是程序代码,网上搜的例子: 注意事项:32 ...

  2. ubuntu 输入用户名密码又回到登陆界面

    问题描述: 输入正确的用户名密码,登陆后又返回登陆界面,重复出现. 问题解决: 环境变量出错,重新配置环境变量. 1.进入命令行模式Ctrl+Alt+F*,然后输入用户名密码: 2.登进去之后,以管理 ...

  3. G1 GC技术解析

    介绍 G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用.G1收集器是工作在堆内不同分区上的收集器,分区既可以是年轻代也可以是老年代 ...

  4. Java不走弯路教程(6.JDBC)

    6.JDBC 在上一章,我们完成了MyDb数据库的简单的客户段调用.作为产品我们还封装了驱动程序,并且提供了统一的调用接口. 大家应该知道,市面上有多种数据库产品,比如Oracle,Mysql,DB2 ...

  5. Mongodb3.6 基操命令(二)——如何使用help

    前言 在上一篇文章Mongodb3.6 快速入门(一)中,我们主要使用两个命令: 1.mongod #启动服务 2.mongo #连接mongodb 对于刚接触mongo的人来说,该怎么给命令传递参数 ...

  6. 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  7. Day9 进程同步锁 进程队列 进程池 生产消费模型 进程池 paramike模块

    进程同步锁: 当运行程序的时候,有可能你的程序同时开多个进程,开进程的时候会将多个执行结果打印出来,这样的话打印的信息都是错乱的,怎么保证打印信息是有序的呢? 其实也就是相当于让进程独享资源. fro ...

  8. Android hybrid App项目构建和部分基本开发问题

    1.首先是选型:Cordova+Ionic Framework,调试测试环境是Ripple Emulator.开发环境其实可以随便选,我个人选择了Eclipse,当然Android SDK+ADT也是 ...

  9. RPi:QT+wiringPi demo程序

    一个项目里面要用到这玩意儿,网上查了几篇文章凑出来最后还是不行,自己灵机一动就成了. 今天再次搜索的时候,发现另一篇文章已经讲明白了,真是欲哭无泪 程序大部分参考的是之前学qt的摸索出来的,其实只要在 ...

  10. C#避免踩坑之如何添加paint事件

    看截图: 首先,右击->属性 然后出来这个界面. 接下来,注意看这个界面的上面:鼠标悬停这个闪电符号,看到没,事件!! 那个闪电符号,点它! 然后下拉找到这个: 你要事先在代码里面添加Form1 ...