[TOC]
## 概述
有時,我們需要瀏覽器處理網頁,但并不需要瀏覽,比如生成網頁的截圖、抓取網頁數據等操作。[PhantomJS](http://phantomjs.org/)的功能,就是提供一個瀏覽器環境的命令行接口,你可以把它看作一個“虛擬瀏覽器”,除了不能瀏覽,其他與正常瀏覽器一樣。它的內核是WebKit引擎,不提供圖形界面,只能在命令行下使用,我們可以用它完成一些特殊的用途。
PhantomJS是二進制程序,需要[安裝](http://phantomjs.org/download.html)后使用。
~~~
$ npm install phantomjs -g
~~~
使用下面的命令,查看是否安裝成功。
~~~
$ phantomjs --version
~~~
## REPL環境
phantomjs提供了一個完整的REPL環境,允許用戶通過命令行與PhantomJS互動。鍵入phantomjs,就進入了該環境。
~~~
$ phantomjs
~~~
這時會跳出一個phantom提示符,就可以輸入Javascript命令了。
~~~
phantomjs> 1+2
3
phantomjs> function add(a,b) { return a+b; }
undefined
phantomjs> add(1,2)
3
~~~
按ctrl+c可以退出該環境。
下面,我們把上面的add()函數寫成一個文件add.js文件。
~~~
// add.js
function add(a,b){ return a+b; }
console.log(add(1,2));
phantom.exit();
~~~
上面的代碼中,console.log()的作用是在終端窗口顯示,phantom.exit()則表示退出phantomjs環境。一般來說,不管什么樣的程序,exit這一行都不能少。
現在,運行該程序。
~~~
$ phantomjs add.js
~~~
終端窗口就會顯示結果為3。
下面是更多的例子。
~~~
phantomjs> phantom.version
{
"major": 1,
"minor": 5,
"patch": 0
}
phantomjs> console.log("phantom is awesome")
phantom is awesome
phantomjs> window.navigator
{
"cookieEnabled": true,
"language": "en-GB",
"productSub": "20030107",
"product": "Gecko",
// ...
}
~~~
## webpage模塊
webpage模塊是PhantomJS的核心模塊,用于網頁操作。
~~~
var webPage = require('webpage');
var page = webPage.create();
~~~
上面代碼表示加載PhantomJS的webpage模塊,并創建一個實例。
下面是webpage實例的屬性和方法介紹。
### open()
open方法用于打開具體的網頁。
~~~
var page = require('webpage').create();
page.open('http://slashdot.org', function (s) {
console.log(s);
phantom.exit();
});
~~~
上面代碼中,open()方法,用于打開具體的網頁。它接受兩個參數。第一個參數是網頁的網址,這里打開的是著名新聞網站[Slashdot](http://slashdot.org/),第二個參數是回調函數,網頁打開后該函數將會運行,它的參數是一個表示狀態的字符串,如果打開成功就是success,否則就是fail。
注意,只要接收到服務器返回的結果,PhantomJS就會報告網頁打開成功,而不管服務器是否返回404或500錯誤。
open方法默認使用GET方法,與服務器通信,但是也可以使用其他方法。
~~~
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方法,第三個參數用來指定該方法所要使用的數據。
open方法還允許提供配置對象,對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()
evaluate方法用于打開網頁以后,在頁面中執行JavaScript代碼。
~~~
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();
});
~~~
網頁內部的console語句,以及evaluate方法內部的console語句,默認不會顯示在命令行。這時可以采用onConsoleMessage回調函數,上面的例子可以改寫如下。
~~~
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();
});
~~~
上面代碼中,evaluate方法內部有console語句,默認不會輸出在命令行。這時,可以用onConsoleMessage方法監聽這個事件,進行處理。
### includeJs()
includeJs方法用于頁面加載外部腳本,加載結束后就調用指定的回調函數。
~~~
var page = require('webpage').create();
page.open('http://www.sample.com', function() {
page.includeJs("http://path/to/jquery.min.js", function() {
page.evaluate(function() {
$("button").click();
});
phantom.exit()
});
});
~~~
上面的例子在頁面中注入jQuery腳本,然后點擊所有的按鈕。需要注意的是,由于是異步加載,所以`phantom.exit()`語句要放在`page.includeJs()`方法的回調函數之中,否則頁面會過早退出。
### 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();
});
~~~
該方法還可以接受一個配置對象,format字段用于指定圖片格式,quality字段用于指定圖片質量,最小為0,最大為100。
### viewportSize,zoomFactor
viewportSize屬性指定瀏覽器視口的大小,即網頁加載的初始瀏覽器窗口大小。
~~~
var webPage = require('webpage');
var page = webPage.create();
page.viewportSize = {
width: 480,
height: 800
};
~~~
viewportSize的Height字段必須指定,不可省略。
zoomFactor屬性用來指定渲染時(render方法和renderBase64方法)頁面的放大系數,默認是1(即100%)。
~~~
var webPage = require('webpage');
var page = webPage.create();
page.zoomFactor = 0.25;
page.render('capture.png');
~~~
### onResourceRequested
onResourceRequested屬性用來指定一個回調函數,當頁面請求一個資源時,會觸發這個回調函數。它的第一個參數是HTTP請求的元數據對象,第二個參數是發出的網絡請求對象。
HTTP請求包括以下字段。
* id:所請求資源的編號
* method:使用的HTTP方法
* url:所請求的資源 URL
* time:一個包含請求時間的Date對象
* headers:HTTP頭信息數組
網絡請求對象包含以下方法。
* 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));
};
~~~
### onResourceReceived
onResourceReceived屬性用于指定一個回調函數,當網頁收到所請求的資源時,就會執行該回調函數。它的參數就是服務器發來的HTTP回應的元數據對象,包括以下字段。
* id:所請求的資源編號
* url:所請求的資源的URL r- time:包含HTTP回應時間的Date對象
* headers:HTTP頭信息數組
* bodySize:解壓縮后的收到的內容大小
* contentType:接到的內容種類
* redirectURL:重定向URL(如果有的話)
* stage:對于多數據塊的HTTP回應,頭一個數據塊為start,最后一個數據塊為end。
* status:HTTP狀態碼,成功時為200。
* statusText:HTTP狀態信息,比如OK。
如果HTTP回應非常大,分成多個數據塊發送,onResourceReceived會在收到每個數據塊時觸發回調函數。
~~~
var webPage = require('webpage');
var page = webPage.create();
page.onResourceReceived = function(response) {
console.log('Response (#' + response.id + ', stage "' + response.stage + '"): ' + JSON.stringify(response));
};
~~~
## system模塊
system模塊可以加載操作系統變量,system.args就是參數數組。
~~~
var page = require('webpage').create(),
system = require('system'),
t, address;
// 如果命令行沒有給出網址
if (system.args.length === 1) {
console.log('Usage: page.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 time ' + t + ' ms');
}
phantom.exit();
});
~~~
使用方法如下:
~~~
$ phantomjs page.js http://www.google.com
~~~
## 應用
Phantomjs可以實現多種應用。
### 過濾資源
處理頁面的時候,有時不希望加載某些特定資源。這時,可以對URL進行匹配,一旦符合規則,就中斷對資源的連接。
~~~
page.onResourceRequested = function(requestData, request) {
if ((/http:\/\/.+?\.css$/gi).test(requestData['url'])) {
console.log('Skipping', requestData['url']);
request.abort();
}
};
~~~
上面代碼一旦發現加載的資源是CSS文件,就會使用`request.abort`方法中斷連接。
### 截圖
最簡單的生成網頁截圖的方法如下。
~~~
var page = require('webpage').create();
page.open('http://google.com', function () {
page.render('google.png');
phantom.exit();
});
~~~
page對象代表一個網頁實例;open方法表示打開某個網址,它的第一個參數是目標網址,第二個參數是網頁載入成功后,運行的回調函數;render方法則是渲染頁面,然后以圖片格式輸出,該方法的參數就是輸出的圖片文件名。
除了簡單截圖以外,還可以設置各種截圖參數。
~~~
var page = require('webpage').create();
page.open('http://google.com', function () {
page.zoomFactor = 0.25;
console.log(page.renderBase64());
phantom.exit();
});
~~~
zoomFactor表示將截圖縮小至原圖的25%大小;renderBase64方法則是表示將截圖(PNG格式)編碼成Base64格式的字符串輸出。
下面的例子則是使用了更多參數。
~~~
// page.js
var page = require('webpage').create();
page.settings.userAgent = 'WebKit/534.46 Mobile/9A405 Safari/7534.48.3';
page.settings.viewportSize = { width: 400, height: 600 };
page.open('http://slashdot.org', function (status) {
if (status !== 'success') {
console.log('Unable to load!');
phantom.exit();
} else {
var title = page.evaluate(function () {
var posts = document.getElementsByClassName("article");
posts[0].style.backgroundColor = "#FFF";
return document.title;
});
window.setTimeout(function () {
page.clipRect = { top: 0, left: 0, width: 600, height: 700 };
page.render(title + "1.png");
page.clipRect = { left: 0, top: 600, width: 400, height: 600 };
page.render(title + '2.png');
phantom.exit();
}, 1000);
}
});
~~~
上面代碼中的幾個屬性和方法解釋如下:
* settings.userAgent:指定HTTP請求的userAgent頭信息,上面例子是手機瀏覽器的userAgent。
* settings.viewportSize:指定瀏覽器窗口的大小,這里是400x600。
* evaluate():用來在網頁上運行Javascript代碼。在這里,我們抓取第一條新聞,然后修改背景顏色,并返回該條新聞的標題。
* clipRect:用來指定網頁截圖的大小,這里的截圖左上角從網頁的(0\. 0)坐標開始,寬600像素,高700像素。如果不指定這個值,就表示對整張網頁截圖。
* render():根據clipRect的范圍,在當前目錄下生成以第一條新聞的名字命名的截圖。
### 抓取圖片
使用官方網站提供的[rasterize.js](https://github.com/ariya/phantomjs/blob/master/examples/rasterize.js),可以抓取網絡上的圖片,將起保存在本地。
~~~
phantomjs rasterize.js http://ariya.github.com/svg/tiger.svg tiger.png
~~~
使用[rasterize.js](https://github.com/ariya/phantomjs/blob/master/examples/rasterize.js),還可以將網頁保存為pdf文件。
~~~
phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf
~~~
### 生成網頁
phantomjs可以生成網頁,使用content方法指定網頁的HTML代碼。
~~~
var page = require('webpage').create();
page.viewportSize = { width: 400, height : 400 };
page.content = '<html><body><canvas id="surface"></canvas></body></html>';
phantom.exit();
~~~
官方網站有一個[例子](https://github.com/ariya/phantomjs/blob/master/examples/colorwheel.js),通過創造svg圖片,然后截圖保存成png文件。

## 參考鏈接
* [Testing JavaScript with PhantomJS](http://net.tutsplus.com/tutorials/javascript-ajax/testing-javascript-with-phantomjs/)
* [Phantom Quick Start](https://github.com/ariya/phantomjs/wiki/Quick-Start)
* Ariya Hidayat,?[Web Page Clipping with PhantomJS](http://ariya.ofilabs.com/2013/04/web-page-clipping-with-phantomjs.html)
* BenjaminBenBen,?[Using PhantomJS WebServer](http://benjaminbenben.com/2013/07/28/phantomjs-webserver/)
* Ariya Hidayat,?[Capturing Web Page Without Stylesheets](http://ariya.ofilabs.com/2013/06/capturing-web-page-without-stylesheets.html): 過濾CSS文件
- 第一章 導論
- 1.1 前言
- 1.2 為什么學習JavaScript?
- 1.3 JavaScript的歷史
- 第二章 基本語法
- 2.1 語法概述
- 2.2 數值
- 2.3 字符串
- 2.4 對象
- 2.5 數組
- 2.6 函數
- 2.7 運算符
- 2.8 數據類型轉換
- 2.9 錯誤處理機制
- 2.10 JavaScript 編程風格
- 第三章 標準庫
- 3.1 Object對象
- 3.2 Array 對象
- 3.3 包裝對象和Boolean對象
- 3.4 Number對象
- 3.5 String對象
- 3.6 Math對象
- 3.7 Date對象
- 3.8 RegExp對象
- 3.9 JSON對象
- 3.10 ArrayBuffer:類型化數組
- 第四章 面向對象編程
- 4.1 概述
- 4.2 封裝
- 4.3 繼承
- 4.4 模塊化編程
- 第五章 DOM
- 5.1 Node節點
- 5.2 document節點
- 5.3 Element對象
- 5.4 Text節點和DocumentFragment節點
- 5.5 Event對象
- 5.6 CSS操作
- 5.7 Mutation Observer
- 第六章 瀏覽器對象
- 6.1 瀏覽器的JavaScript引擎
- 6.2 定時器
- 6.3 window對象
- 6.4 history對象
- 6.5 Ajax
- 6.6 同域限制和window.postMessage方法
- 6.7 Web Storage:瀏覽器端數據儲存機制
- 6.8 IndexedDB:瀏覽器端數據庫
- 6.9 Web Notifications API
- 6.10 Performance API
- 6.11 移動設備API
- 第七章 HTML網頁的API
- 7.1 HTML網頁元素
- 7.2 Canvas API
- 7.3 SVG 圖像
- 7.4 表單
- 7.5 文件和二進制數據的操作
- 7.6 Web Worker
- 7.7 SSE:服務器發送事件
- 7.8 Page Visibility API
- 7.9 Fullscreen API:全屏操作
- 7.10 Web Speech
- 7.11 requestAnimationFrame
- 7.12 WebSocket
- 7.13 WebRTC
- 7.14 Web Components
- 第八章 開發工具
- 8.1 console對象
- 8.2 PhantomJS
- 8.3 Bower:客戶端庫管理工具
- 8.4 Grunt:任務自動管理工具
- 8.5 Gulp:任務自動管理工具
- 8.6 Browserify:瀏覽器加載Node.js模塊
- 8.7 RequireJS和AMD規范
- 8.8 Source Map
- 8.9 JavaScript 程序測試
- 第九章 JavaScript高級語法
- 9.1 Promise對象
- 9.2 有限狀態機
- 9.3 MVC框架與Backbone.js
- 9.4 嚴格模式
- 9.5 ECMAScript 6 介紹
- 附錄
- 10.1 JavaScript API列表
- 草稿一:函數庫
- 11.1 Underscore.js
- 11.2 Modernizr
- 11.3 Datejs
- 11.4 D3.js
- 11.5 設計模式
- 11.6 排序算法
- 草稿二:jQuery
- 12.1 jQuery概述
- 12.2 jQuery工具方法
- 12.3 jQuery插件開發
- 12.4 jQuery.Deferred對象
- 12.5 如何做到 jQuery-free?
- 草稿三:Node.js
- 13.1 Node.js 概述
- 13.2 CommonJS規范
- 13.3 package.json文件
- 13.4 npm模塊管理器
- 13.5 fs 模塊
- 13.6 Path模塊
- 13.7 process對象
- 13.8 Buffer對象
- 13.9 Events模塊
- 13.10 stream接口
- 13.11 Child Process模塊
- 13.12 Http模塊
- 13.13 assert 模塊
- 13.14 Cluster模塊
- 13.15 os模塊
- 13.16 Net模塊和DNS模塊
- 13.17 Express框架
- 13.18 Koa 框架