騰訊雲技術社區-掘金主頁持續爲你們呈現雲計算技術文章,歡迎你們關注!javascript
做者:linkcss
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.(phantomjs.org/)html
PhantomJS是一個無界面的webkit內核瀏覽器,你能夠把它看成一個沒有界面的Safari。java
目前PhantomJS的最新版本的2.0,官方文檔中有提到說:若是在使用老版本時碰到一些難解的bug,能夠升級到最新版試試。mysql
直接下載phantomjs-2.0.0-windows.zip,並解壓,將bin文件夾中的可執行文件phantomjs.exe的路徑添加到環境變量後(可能須要重啓機器才能生效),就能夠在命令行環境(cmd或cygwin)中使用phantomjs命令執行js文件了。jquery
能夠在Bitbucket下載已經編譯好的二進制文件安裝包,不過目前Linux提供到PhantomJS 1.9.8的安裝包,最新的PhantomJS 2.0尚未發佈。
安裝方式:linux
> cd /usr/local
> tar zxvf phantomjs-1.9.8-linux-x86_64.tar.bz2複製代碼
> ln -sf phantomjs-1.9.8-linux-x86_64/bin/phantomjs phantomjs複製代碼
因爲WebKit模塊中有數千個文件,所以由源碼編譯PhantomJS會花費很長的時間,文檔上說,開四個並行的進程進行編譯工做,須要超過30分鐘的時間,所以官方文檔推薦直接下載和安裝二進制文件。
具體的安裝方法,這裏就再也不贅述,你們能夠到官方文檔上查看。web
咱們可使用下面的命令來查看PhantomJS是否安裝成功:ajax
> phantomjs -v複製代碼
命令運行phantomjs xxx.js便可執行一個PhantomJS程序。正則表達式
webpage是PhantomJS的核心模塊,你能夠經過如下方式,得到一個webpage模塊的實例:
var webPage = require("webpage"),
page = webPage.create();複製代碼
打開一個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...
});複製代碼
在打開一個網頁後,咱們每每有對其進行操做的需求,例如模擬點擊登錄按鈕、獲取某個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()方法加載外部腳本,例如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方法能夠根據文件名的後綴將圖片保存成對應的格式。目前支持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()方法的回調函數。回調函數接受兩個參數,第一個參數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屬性用於指定一個回調函數,當網頁收到所請求的資源時,就會執行該回調函數。回調函數只有一個參數,就是所請求資源的服務器發來的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是一個閹割版的webkit,不支持flash、webGL、video/audio、css 3-d,phontomjs不想揹負操做系統強相關的特性,跨平臺比較困難。
- 若是使用Page模塊的onResourceReceived()方法監聽頁面收到的請求資源,是沒法獲得該資源的response.body的,這也是目前PhantomJS最受開發者吐槽的點之一。
相關推薦
騰訊雲上PhantomJS用法示例
騰訊雲上Selenium用法示例
包學會之淺入淺出Vue.js:開學篇
此文已由做者受權騰訊雲技術社區發佈,轉載請註明文章出處
原文連接:www.qcloud.com/community/a…
獲取更多騰訊海量技術實踐乾貨,歡迎你們前往騰訊雲技術社區