歡迎大家前往騰訊雲技術社群,獲取更多騰訊海量技術實踐乾貨哦~
作者:李大偉
Phantomjs簡介
什麼是Phantomjs
Phantomjs官網介紹是:不需要瀏覽器的完整web協議棧(Full web stack No browser required),也就是常說的無頭瀏覽器——或者好聽點叫做:無介面的web解析器。
Phantomjs的特點
由於“無頭”——免去了渲染視覺化的網頁介面,她的速度要比一般的瀏覽器快不少,又因為她是完整的web協議棧,所以不僅僅提供了JavaScript API,還完整的支援各類web標準:DOM操作、CSS選擇器、JSON、Canvas和SVG,以及檔案系統API和作業系統API。此外她還提供很多web事件監控處理介面(event handle),這一點也是Phantomjs區別於selenium等web自動化測試工具的關鍵所在,具體的將在後續安全檢測中詳細說明。
筆者將Phantomjs的特徵彙總如下表:
Phantomjs提供的API彙總
The WebPage API
- HTML documents
- DOM
- Handle cookies
- Handle events
- Send requests
- Receive responces
- Wait For AJAX
- User Interaction
- Render Full Images
The System API
- Get OS information
- command-line
The FileSystem API
- Read data from file
- Writing JSON data to file
The WebServer API
- process client requests
小結
鑑於以上特點,我們就會發現Phantomjs特別適合用來寫!爬!蟲!
支援JavaScript便可以動態載入資源,或完成一些模擬人類的動作;支援DOM操作便可以結構化頁面;CSS的支援便可以快捷方便的完成頁面文件的渲染,供我們儲存圖片或到處PDF;支援JSON、Canvas和SVG更是對與資料或多媒體頁面處理的加分項;同時檔案系統API的提供,也讓我們很方便的將處理結果格式化儲存起來。
Phantomjs常見的用法
1: 互動模式/REPL/Interactive mode
下載Phantomjs後,直接執行Phantomjs就進入了互動模式,這時我們可以把她當做一個JavaScript直譯器使用,運算、js方法、使用window.navigator物件檢視“瀏覽器”資訊等等,大家如果安裝了Phantomjs可以隨意輸入一些命令感受一下。感受結束後輸入phantom.exit()`退出。
圖:REPL 模式下的 Phantomjs
如果是初學js的同學,這個模式可能會比chrome的console欄更大一些,方便用來練習js命令。此外,這個這個模式並不常用,我們更多的是將Phantomjs看做一個二進位制工具來使用。
2: 作為一個二進位制工具
這也是Phantomjs最常用的一個模式:phantomjs /scripts/somejavascript.js來執行一個JavaScript指令碼。指令碼中可以使用Phantomjs提供的各類API(KM的markdown語法不支援頁內錨點,詳見文章前部分的“Phantomjs提供的API彙總”);
開啟頁面
建立一個webpage的例項,然後使用open方法開啟騰訊網首頁,如果返回值是成功,則日誌列印出網頁標題,之後退出。
/****************************************************************
* create an instance of the webpage module
* get the page and echo the page's title
* file: somejavascript.js
* auther : Taerg
* date : 12/05/2017
*****************************************************************/
var page = require('webpage').create();
// open the webpage
// defined callback: check the status and echo teh status
page.open("http://www.qq.com", function(status) {
if ( status === "success" ) {
console.log("Page load success.The page title is:");
console.log(page.title);
} else {
console.log("Page load failed.");
}
phantom.exit(0);
});
複製程式碼
獲取cookie
當然,我們也可以用page.content來獲取頁面的所有內容,使用page.cookies來獲取cookie。
如下,我們獲取訪問王者榮耀網站時的cookie,並使用鍵值對的方式列印在log裡:
/****************************************************************
* create an instance of the webpage module
* echo the cookies
* auther : Taerg
* date : 12/05/2017
*****************************************************************/
var page = require('webpage').create();
console.log(1);
page.open("http://www.qq.com/", function(status) {
if (status === 'success') {
var cookies = page.cookies;
for(var i in cookies) {
console.log(cookies[i].name + '=' + cookies[i].value);
}
}
phantom.exit(0);
});
複製程式碼
對應的輸出為:
圖:phantomjs_getcookie
執行JavaScript
Phantomjs作為無頭“瀏覽器“,當然對JavaScript的支援也是極好的。如下,我們定義了一個簡單的函式,來獲取頁面標題後返回。只需要簡單的呼叫page.evaluate()來執行這段JavaScript程式碼即可。
/****************************************************************
* create an instance of the webpage module
* include system module
* auther : Taerg
* date : 12/05/2017
*****************************************************************/
var system = require('system');
var url = system.args[1];
console.log(url);
var page = require('webpage').create();
page.open(url, function(status) {
if ( status === "success" ) {
var title = page.evaluate(function () {
return document.title;
});
console.log(title);
}
})
phantom.exit(0);
複製程式碼
使用第三方js庫(如jQuery)
如果覺得自己用JavaScript程式碼來重複造輪子太麻煩,我們也可以在Phantomjs中使用第三方的JavaScript庫。Phantomjs為我們提供了2中使用第三方庫的方法:
- 方法一:includeJs()
- 方法二:injectJs()
二者常常混用,主要的區別在於injectJs是阻塞載入,而includeJs是動態載入。injectJs可以理解為程式碼執行到這裡時,程式阻塞,載入這個js檔案到記憶體後,程式繼續執行,在操作頁面時不會對這個檔案發起請求。而includeJs則是在載入頁面用到此js檔案時動態載入檔案。
例項程式碼如下:
/****************************************************************
* create an instance of the webpage module
* load third part js lib
* auther : Taerg
* date : 12/05/2017
*****************************************************************/
var page = require('webpage').create();
// open the webpage
page.open("http://www.qq.com", function(status) {
page.injectJs('jquery321.js');
//different with page.includeJs
if (status === 'success') {
var aoffset = page.evaluate(function() {
return (typeof jQuery === 'function') ? jQuery.fn.jquery : undefined;
});
console.log(aoffset);
}else{
console.log('open error');
}
phantom.exit(0);
});
複製程式碼
輸出如下:
我們先inject了版本號為3.2.1的本地jQuery檔案,之後便可以使用jQuery的方法來檢視jQuery版本。當然,這只是驗證jQuery載入成功,在我們完全可以使用其他jQuery提供快捷方法來實現我們的需求。
儲存指定頁面區間截圖
在我們處理頁面時,常常會有儲存頁面截圖的需求,比如:儲存頁面BUG的樣子、關鍵資訊的留證等等。這時我們就可以使用Phantomjs的page提供的render方法,她支援將完整的頁面(自動滾屏截圖)、指定區間的頁面儲存下來(.png, .pdf, .jpg等格式均支援)。
如下,我們想獲取天氣網站”我的天氣“詳情,而不去關注網頁其他各種新聞和廣告,我們只需指定區間,然後儲存截圖即可:
/****************************************************************
* phjs_clip.js
* get the weather pic
* auther : Taerg
* date : 12/05/2017
*****************************************************************/
var page = new WebPage();
page.open('http://www.weather.com.cn', function (status) {
if (status !== 'success') {
output.error = 'Unable to access network';
} else {
page.clipRect = {
top: 200,
left: 750,
width: 300,
height: 500
}
page.render('weather.png');
console.log('Capture saved');
}
phantom.exit();
});
複製程式碼
儲存的圖片如下所示:
圖:phantom_get_weather
三行程式碼怒懟”反爬蟲”
正常使用者訪問
當我們正常使用瀏覽器訪問https://media.om.qq.com/media/5054676/list時,一切正常,如下圖:
圖:safari_get_omqq
根據這套反爬蟲作者的解釋,客戶端經過JavaScript計算出來一個票據,包含在cookie將在服務端再次驗證,驗證通過則返回資料,驗證不通過則不返回資料。如下圖所示:
圖:anti_spide
下面我們通過指令碼來自動拉去這個頁面資料試試
普通靜態爬蟲
- curl get
首先我們先用最簡答的curl來get這個頁面看看能都拿到這個頁面的資料:
圖: curl_get_omqq
如上圖所示,被反爬蟲系統攔截了。
我們再用Python試試,使用最通用的“HTTP for humans”的requests.get請求:
圖: request_get_omqq
可以看到依舊會被反爬蟲機制攔截。
反爬蟲原理分析
通過人工瀏覽器訪問、抓包分析,我們可以看到:
1 . 人工訪問這個網頁一共發起了6條請求 2 . 第1條請求時直接請求目標url,由於沒有合法票據,返回403。同時在403頁面中包含了2個JavaScript檔案
圖: load_js
3 .接下來的2個請求分別為對403頁面中的JavaScript指令碼進行載入
4 .載入執行完畢後,獲得了合法票據並新增進cookie中再次發起請求,產生了第4條請求。如下圖:
圖:omqq_signiture
5.第4條請求帶有合法票據,因此沒有被403forbidden掉,而是增加一個客戶id標示後302跳轉到了資料頁面。如下圖:Set-cookie中新增了id簽名。
圖: redirect
6 .此時,cookie中已經包含有了合法的簽名以及客戶id,請求到了JSON資料。得到了正常的頁面:
圖: safafi_get)omqq
基於Phantomjs的動態爬蟲
至此,我們就可以根據前面的分析使用Phantomjs來逐步模擬人工請求,從而繞過反爬蟲系統。先看程式碼:
/****************************************************************
* phjs_antispider.js
* anti-anti-spider script for https://media.om.qq.com/media/5054676/list
* auther : Taerg
* date : 12/05/2017
*****************************************************************/
var page = require("webpage").create();
var system = require("system")
url = system.args[1];
headers = {};
page.customHeaders = headers;
page.settings = {
javascriptEnabled: true,
userAgent: 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36',
};
page.viewportSize = {
width: 1024,
height: 768
};
page.open(url,
function(status) {
page.injectJs('jquery321.js');
if (status !== 'success') {
console.log('Unable to access network');
} else {
page.evaluate(function() {
var allElements = $('*');
for ( var i = 0; i<allElements.length; i++ ) {
if (allElements[i].href) {
javascript_code = allElements[i].href.match("javascript:(.+)");
if (javascript_code){
console.log(javascript_code[0]);
eval(javascript_code[0]);
}
}
}
});
}
window.setTimeout(
function() {
console.log("crawl_content:"+page.content+"content_end")
phantom.exit()
},
1000
);
phantom.exit();
});
複製程式碼
在上述程式碼中:
- 我們先修改page.settings,設定請用JavaScript,
- 同時自定義user-agent,偽造瀏覽器,
- 設定解析度,進一步偽造人工瀏覽,
- 開啟頁面時引入jQuery檔案,
- 使用jQuery的選擇器選出頁面中的所有元素,
- 如果元素中存在JavaScript指令碼,則執行這些指令碼,
- 設定頁面超時時間,並列印出頁面內容。
執行結果如下:可見,我們的請求已經繞過了反爬蟲機制。
圖: phantomjs_get_omqq
3行程式碼爬取:基於Casperjs的類人動態爬蟲
臥槽,我就是個開發,你跟我說抓包分析啥的我不會啊!!寶寶只想爬點資料而已啊…
那就用三行程式碼來實現吧:
- 第一行建立一個casper例項
- 第二行發起請求
- 第三行執行並退出
/****************************************************************
* crawl the anti-aipder website: om.qq.com
* auther : Taerg
* date : 17/05/2017
*****************************************************************/
var casper = require("casper").create();
casper.start('https://media.om.qq.com/media/5054676/list', function() {
require('utils').dump(JSON.parse(this.getPageContent()));
});
casper.run(function() {
this.exit();
});
複製程式碼
結果如下:
圖:casper_get_omqq
這三行程式碼不僅成功繞過了反爬蟲的限制,而且自帶的JSON方法也將也資料結構化顯示(儲存),對於複雜爬蟲的開發可以極大的簡化開發複雜度。
這三行程式碼中用到的就是—CasperJS。
CasperJS官方自稱是一個開源的導航指令碼和測試工具,但實際用起來爽的不行不行的。具體包括:
- defining & ordering navigation steps * filling forms
- clicking links
- capturing screenshots of a page (or an area)
- making assertions on remote DOM
- logging & events
- downloading resources, even binary ones
- catching errors and react accordingly
- writing functional test suites, exporting results as JUnit XML (xUnit)
此外,CasperJS最為強大的地方在於我在這裡給大家簡單介紹之後,我就不用再說什麼了,CasperJS擁有極其豐富的文件及例項程式碼。這一點對比核心文件還是TODO,需要我們來撰寫各類文件的Phantomjs來說友好太多了。
###相關閱讀
爬蟲實戰:爬蟲之 web 自動化終極殺手 ( 上) Scrapy 對接 Splash 精通 Python 網路爬蟲:網路爬蟲學習路線 此文已由作者授權騰訊雲技術社群釋出,轉載請註明文章出處
原文連結: https://www.qcloud.com/community/article/636391