反-反爬蟲:用幾行代碼寫出和人類一樣的動態爬蟲
歡迎大家前往騰訊雲技術社區,獲取更多騰訊海量技術實踐幹貨哦~
作者:李大偉
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的特征匯總如下表:
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來說友好太多了。
最後,鑒於CasperJS擁有的豐富的文檔,我也就不再班門弄斧了,本文就此打住。下次和大家分享討論基於Phantomjs的XSS檢測工具。
相關閱讀
爬蟲實戰:爬蟲之 web 自動化終極殺手 ( 上)
數據運營實戰(三):用數據說話,從埋點開始
精通 Python 網絡爬蟲:網絡爬蟲學習路線
此文已由作者授權騰訊雲技術社區發布,轉載請註明文章出處
原文鏈接:https://cloud.tencent.com/community/article/636391
反-反爬蟲:用幾行代碼寫出和人類一樣的動態爬蟲