反 反爬蟲:用幾行程式碼寫出和人類一樣的動態爬蟲

weixin_34402408發表於2017-12-19

歡迎大家前往騰訊雲技術社群,獲取更多騰訊海量技術實踐乾貨哦~

作者:李大偉

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();
});
複製程式碼

在上述程式碼中:

  1. 我們先修改page.settings,設定請用JavaScript,
  2. 同時自定義user-agent,偽造瀏覽器,
  3. 設定解析度,進一步偽造人工瀏覽,
  4. 開啟頁面時引入jQuery檔案,
  5. 使用jQuery的選擇器選出頁面中的所有元素,
  6. 如果元素中存在JavaScript指令碼,則執行這些指令碼,
  7. 設定頁面超時時間,並列印出頁面內容。

執行結果如下:可見,我們的請求已經繞過了反爬蟲機制。

圖: phantomjs_get_omqq

3行程式碼爬取:基於Casperjs的類人動態爬蟲

臥槽,我就是個開發,你跟我說抓包分析啥的我不會啊!!寶寶只想爬點資料而已啊…

那就用三行程式碼來實現吧:

  1. 第一行建立一個casper例項
  2. 第二行發起請求
  3. 第三行執行並退出

/****************************************************************
* 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官方自稱是一個開源的導航指令碼和測試工具,但實際用起來爽的不行不行的。具體包括:

此外,CasperJS最為強大的地方在於我在這裡給大家簡單介紹之後,我就不用再說什麼了,CasperJS擁有極其豐富的文件及例項程式碼。這一點對比核心文件還是TODO,需要我們來撰寫各類文件的Phantomjs來說友好太多了。

###相關閱讀

爬蟲實戰:爬蟲之 web 自動化終極殺手 ( 上) Scrapy 對接 Splash 精通 Python 網路爬蟲:網路爬蟲學習路線 此文已由作者授權騰訊雲技術社群釋出,轉載請註明文章出處

原文連結: https://www.qcloud.com/community/article/636391

相關文章