1. 程式人生 > >反-反爬蟲:用幾行代碼寫出和人類一樣的動態爬蟲

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

簽名 lib rgs 常見 todo 只需要 website 結束 pro

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

作者:李大偉

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);
});

當然,我們也可以用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官方自稱是一個開源的導航腳本和測試工具,但實際用起來爽的不行不行的。具體包括:

  • 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

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