PhantomJS:可編程無頭瀏覽器

2021-09-15 16:14 更新

概述

有時,我們需要瀏覽器處理網(wǎng)頁,但并不需要瀏覽,比如生成網(wǎng)頁的截圖、抓取網(wǎng)頁數(shù)據(jù)等操作。PhantomJS的功能,就是提供一個瀏覽器環(huán)境的命令行接口,你可以把它看作一個“虛擬瀏覽器”,除了不能瀏覽,其他與正常瀏覽器一樣。它的內(nèi)核是WebKit引擎,不提供圖形界面,只能在命令行下使用,我們可以用它完成一些特殊的用途。

PhantomJS是二進制程序,需要安裝后使用。

$ npm install phantomjs -g

使用下面的命令,查看是否安裝成功。

$ phantomjs --version

REPL環(huán)境

phantomjs提供了一個完整的REPL環(huán)境,允許用戶通過命令行與PhantomJS互動。鍵入phantomjs,就進入了該環(huán)境。

$ phantomjs

這時會跳出一個phantom提示符,就可以輸入Javascript命令了。

phantomjs> 1+2
3

phantomjs> function add(a,b) { return a+b; }
undefined

phantomjs> add(1,2)
3

按ctrl+c可以退出該環(huán)境。

下面,我們把上面的add()函數(shù)寫成一個文件add.js文件。

// add.js

function add(a,b){ return a+b; }

console.log(add(1,2));

phantom.exit();

上面的代碼中,console.log()的作用是在終端窗口顯示,phantom.exit()則表示退出phantomjs環(huán)境。一般來說,不管什么樣的程序,exit這一行都不能少。

現(xiàn)在,運行該程序。

$ phantomjs add.js

終端窗口就會顯示結(jié)果為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的核心模塊,用于網(wǎng)頁操作。

var webPage = require('webpage');
var page = webPage.create();

上面代碼表示加載PhantomJS的webpage模塊,并創(chuàng)建一個實例。

下面是webpage實例的屬性和方法介紹。

open()

open方法用于打開具體的網(wǎng)頁。

var page = require('webpage').create();

page.open('http://slashdot.org', function (s) {
  console.log(s);
  phantom.exit();
});

上面代碼中,open()方法,用于打開具體的網(wǎng)頁。它接受兩個參數(shù)。第一個參數(shù)是網(wǎng)頁的網(wǎng)址,這里打開的是著名新聞網(wǎng)站Slashdot,第二個參數(shù)是回調(diào)函數(shù),網(wǎng)頁打開后該函數(shù)將會運行,它的參數(shù)是一個表示狀態(tài)的字符串,如果打開成功就是success,否則就是fail。

注意,只要接收到服務(wù)器返回的結(jié)果,PhantomJS就會報告網(wǎng)頁打開成功,而不管服務(wù)器是否返回404或500錯誤。

open方法默認使用GET方法,與服務(wù)器通信,但是也可以使用其他方法。

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方法向服務(wù)器發(fā)送數(shù)據(jù)。open方法的第二個參數(shù)用來指定HTTP方法,第三個參數(shù)用來指定該方法所要使用的數(shù)據(jù)。

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方法用于打開網(wǎng)頁以后,在頁面中執(zhí)行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();
});

網(wǎng)頁內(nèi)部的console語句,以及evaluate方法內(nèi)部的console語句,默認不會顯示在命令行。這時可以采用onConsoleMessage回調(diào)函數(shù),上面的例子可以改寫如下。

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方法內(nèi)部有console語句,默認不會輸出在命令行。這時,可以用onConsoleMessage方法監(jiān)聽這個事件,進行處理。

includeJs()

includeJs方法用于頁面加載外部腳本,加載結(jié)束后就調(diào)用指定的回調(diào)函數(shù)。

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()方法的回調(diào)函數(shù)之中,否則頁面會過早退出。

render()

render方法用于將網(wǎng)頁保存成圖片,參數(shù)就是指定的文件名。該方法根據(jù)后綴名,將網(wǎng)頁保存成不同的格式,目前支持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字段用于指定圖片質(zhì)量,最小為0,最大為100。

viewportSize,zoomFactor

viewportSize屬性指定瀏覽器視口的大小,即網(wǎng)頁加載的初始瀏覽器窗口大小。

var webPage = require('webpage');
var page = webPage.create();

page.viewportSize = {
  width: 480,
  height: 800
};

viewportSize的Height字段必須指定,不可省略。

zoomFactor屬性用來指定渲染時(render方法和renderBase64方法)頁面的放大系數(shù),默認是1(即100%)。

var webPage = require('webpage');
var page = webPage.create();

page.zoomFactor = 0.25;
page.render('capture.png');

onResourceRequested

onResourceRequested屬性用來指定一個回調(diào)函數(shù),當(dāng)頁面請求一個資源時,會觸發(fā)這個回調(diào)函數(shù)。它的第一個參數(shù)是HTTP請求的元數(shù)據(jù)對象,第二個參數(shù)是發(fā)出的網(wǎng)絡(luò)請求對象。

HTTP請求包括以下字段。

  • id:所請求資源的編號
  • method:使用的HTTP方法
  • url:所請求的資源 URL
  • time:一個包含請求時間的Date對象
  • headers:HTTP頭信息數(shù)組

網(wǎng)絡(luò)請求對象包含以下方法。

  • abort():終止當(dāng)前的網(wǎng)絡(luò)請求,這會導(dǎo)致調(diào)用onResourceError回調(diào)函數(shù)。
  • changeUrl(newUrl):改變當(dāng)前網(wǎng)絡(luò)請求的URL。
  • setHeader(key, value):設(shè)置HTTP頭信息。
var webPage = require('webpage');
var page = webPage.create();

page.onResourceRequested = function(requestData, networkRequest) {
  console.log('Request (#' + requestData.id + '): ' + JSON.stringify(requestData));
};

onResourceReceived

onResourceReceived屬性用于指定一個回調(diào)函數(shù),當(dāng)網(wǎng)頁收到所請求的資源時,就會執(zhí)行該回調(diào)函數(shù)。它的參數(shù)就是服務(wù)器發(fā)來的HTTP回應(yīng)的元數(shù)據(jù)對象,包括以下字段。

  • id:所請求的資源編號
  • url:所請求的資源的URL r- time:包含HTTP回應(yīng)時間的Date對象
  • headers:HTTP頭信息數(shù)組
  • bodySize:解壓縮后的收到的內(nèi)容大小
  • contentType:接到的內(nèi)容種類
  • redirectURL:重定向URL(如果有的話)
  • stage:對于多數(shù)據(jù)塊的HTTP回應(yīng),頭一個數(shù)據(jù)塊為start,最后一個數(shù)據(jù)塊為end。
  • status:HTTP狀態(tài)碼,成功時為200。
  • statusText:HTTP狀態(tài)信息,比如OK。

如果HTTP回應(yīng)非常大,分成多個數(shù)據(jù)塊發(fā)送,onResourceReceived會在收到每個數(shù)據(jù)塊時觸發(fā)回調(diào)函數(shù)。

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模塊可以加載操作系統(tǒng)變量,system.args就是參數(shù)數(shù)組。

var page = require('webpage').create(),
    system = require('system'),
    t, address;

// 如果命令行沒有給出網(wǎng)址
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

應(yīng)用

Phantomjs可以實現(xiàn)多種應(yīng)用。

過濾資源

處理頁面的時候,有時不希望加載某些特定資源。這時,可以對URL進行匹配,一旦符合規(guī)則,就中斷對資源的連接。

page.onResourceRequested = function(requestData, request) {
  if ((/http:\/\/.+?\.css$/gi).test(requestData['url'])) {
    console.log('Skipping', requestData['url']);
    request.abort();
  }   
};

上面代碼一旦發(fā)現(xiàn)加載的資源是CSS文件,就會使用request.abort方法中斷連接。

截圖

最簡單的生成網(wǎng)頁截圖的方法如下。

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

page對象代表一個網(wǎng)頁實例;open方法表示打開某個網(wǎng)址,它的第一個參數(shù)是目標(biāo)網(wǎng)址,第二個參數(shù)是網(wǎng)頁載入成功后,運行的回調(diào)函數(shù);render方法則是渲染頁面,然后以圖片格式輸出,該方法的參數(shù)就是輸出的圖片文件名。

除了簡單截圖以外,還可以設(shè)置各種截圖參數(shù)。

var page = require('webpage').create();
page.open('http://google.com', function () {
    page.zoomFactor = 0.25;
    console.log(page.renderBase64());
    phantom.exit();
});

zoomFactor表示將截圖縮小至原圖的25%大?。籸enderBase64方法則是表示將截圖(PNG格式)編碼成Base64格式的字符串輸出。

下面的例子則是使用了更多參數(shù)。

// 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():用來在網(wǎng)頁上運行Javascript代碼。在這里,我們抓取第一條新聞,然后修改背景顏色,并返回該條新聞的標(biāo)題。
  • clipRect:用來指定網(wǎng)頁截圖的大小,這里的截圖左上角從網(wǎng)頁的(0. 0)坐標(biāo)開始,寬600像素,高700像素。如果不指定這個值,就表示對整張網(wǎng)頁截圖。
  • render():根據(jù)clipRect的范圍,在當(dāng)前目錄下生成以第一條新聞的名字命名的截圖。

抓取圖片

使用官方網(wǎng)站提供的rasterize.js,可以抓取網(wǎng)絡(luò)上的圖片,將起保存在本地。

phantomjs rasterize.js http://ariya.github.com/svg/tiger.svg tiger.png

使用rasterize.js,還可以將網(wǎng)頁保存為pdf文件。

phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf

生成網(wǎng)頁

phantomjs可以生成網(wǎng)頁,使用content方法指定網(wǎng)頁的HTML代碼。

var page = require('webpage').create();
page.viewportSize = { width: 400, height : 400 };
page.content = '<html><body><canvas id="surface"></canvas></body></html>';
phantom.exit();

官方網(wǎng)站有一個例子,通過創(chuàng)造svg圖片,然后截圖保存成png文件。

2015-07-26/55b4455f2e25e

參考鏈接

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號