程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#基礎知識 >> 如何在使用 RemoteWebDriver 打開網頁的同時獲取 Http 狀態碼

如何在使用 RemoteWebDriver 打開網頁的同時獲取 Http 狀態碼

編輯:C#基礎知識

      最近一直在用Selenium這個開源項目寫一些web 自動化的小玩意。本來一直運行的挺好,直到有一天突然發現資源抓取失敗了,翻看日志才發現,原來本該正常打開的頁面返回了504錯誤所以自然失敗了。如何避免這種情況呢?事實上對於Selenium提供的RemoteWebDriver 來說,一般都是采用下面兩種方式來打開網頁:

 using (var _driver = new PhantomJSDriver())
 {
         _driver.Navigate().GoToUrl("http://hovertree.com/");
         //或
         _driver.Url = "http://hovertree.com/";
 }

      然而這兩種方式都沒有提供相應的API或返回值來判斷網頁是否打開, 所以通常還需要加上一個判斷看看是否出現了指定的頁面元素才能最終確定頁面是否打開:

 try
 {        
      _driver.FindElementById("main-content");
      Console.WriteLine("Page opened.");
 }
 catch (NoSuchElementException)
 {
      Console.WriteLine("Page opened failed.");
 }

      如果需要對所有Open的頁面進行判斷的話,整個代碼結構就會顯得很亂,所以我只是簡單的使用了URL進行判斷,只要_driver返回的URL和我要前往的URL一致的話,就認為頁面打開成功了。但實際上,很多情況下,http出現錯誤的時候頁面並不會redirect

因此URL也不會發生改變。既然不能通過URL進行判斷,那麼久通過http狀態碼來判斷吧?簡單又方便。

      然而,等我翻遍RemoteWebDriver的文檔才發現,RemoteWebDriver本身並不提供獲取Http狀態碼的API,並沒有。。。。。沒有。。。。

      這簡直就是。。。。。。。。。悲劇呀~~~~~~~~~~~~~~

      不過好在大路走不通,咱還可以走小路,經過一番耐心細致的google,還是被我找到了一條曲線救國的方法,就是下面這條:

      注:本方法只適用於 PhantomJSDriver, 其他的driver, 如:ChromeDriver, FireFoxDriver並不能用(當然也許也可以,但是我確實沒有試成功)

     PhantomJSDriver本身是對 PhantomJS的封裝,而PhantomJS是一個基於 WebKit 的服務器端 JavaScript API。它全面支持web而不需浏覽器支持,沒有UI界面,使用起來也比其他帶UI的WebDriver快速。在它的官網上可以看到PhantomJS提供的一系列API, 而我們要使用的就是其中一個:onResourceReceived, 這個API的詳細介紹可以戳這裡, 可以看到在這個回調中,返回的參數是一個response結構體,而其中的response.status 就是我們需要的狀態碼啦。

var webPage = require('webpage');
var page = webPage.create();

page.onResourceReceived = function(response) {
  console.log('Response (#' + response.status + ', stage "' + response.stage + '"): ' + JSON.stringify(response));
};

        好了,思路已經有了,下來就是怎麼實現。要在PhantomJSDriver裡使用上面這段JavaScript就需要用到ExecutePhantomJS()這個方法啦,這也是為什麼我說這個方法只適用於PhantomJSDriver, 因為這個方法是PhantomJSDriver單獨提供的。。。。

怎麼用呢,請看下面這段代碼, 我們用博客園的首頁來試試,看看都加載了那些資源吧。

using (var _driver = new PhantomJSDriver())
{

      const string phantomScript =
            "var page = this; page.onResourceReceived = function(response) {console.log('***** ' + response.id + ' ***** ' + response.url + ' ***** ' + response.stage + ' ***** ' + response.status + ' ***** ' + response.statusText + ' *****');};";
      _driver.ExecutePhantomJS(phantomScript);
      _driver.Navigate().GoToUrl("http://hovertree.com/");
}

        我們把每一個獲取到的response都打印出來看看,還真不少。注意看,有一些response出現了兩遍, 對應的response.stage分別是start和end,這是指什麼呢?官網上也給出了解釋:stage : "start", "end" (FIXME: other value for intermediate chunk?)

        當然對於我們來說,不需要這麼多,只需要知道http://hovertree.com/這個URL本身的狀態,因此我們簡化一下上面的代碼, 過濾到狀態不是end和URL不是http://hovertree.com/的內容。

using (var _driver = new PhantomJSDriver())
{
                const string phantomScript =
                   "var url =  'http://hovertree.com/';" +
                   "var page = this; page.onResourceReceived = function(response) {if (response.stage !== \"end\" || response.url != url) return; console.log('##### ' + response.id + ' ##### ' + response.url + ' ##### ' + response.stage + ' ##### ' + response.status + ' ##### ' + response.statusText + ' #####'); };";              
                _driver.ExecutePhantomJS(phantomScript);
                _driver.Navigate().GoToUrl("http://hovertree.com/");

}

        這下看上去就簡潔多了吧。

        走到這裡,我們已經完成了需求的一大半,成功的獲取到了Http的狀態碼,但是只是打印到console裡面可不行,怎麼能在代碼中取到這個值呢?重定向console輸出然後解析文本?No,No,No,我們還需要更優雅的方法。

        實際上,對於ExecutePhantomJS()這個方法本身來說是可以有返回值的,例如下面的代碼執行之後script的值是2。

string phantomScript = "return 1+1;";
var script = _driver.ExecutePhantomJS(phantomScript);
_driver.Navigate().GoToUrl("http://www.lepan.cc/down2-932746.html");

      因此我最開始的想法很簡單,直接把response返回出來不就好了?就像下面這段代碼:

using (var _driver = new PhantomJSDriver())
{ 
      const string phantomScript =
            "var url =  'http://hovertree.com/';" +
            "var page = this; page.onResourceReceived = function(response) {if (response.stage !== \"end\" || response.url != url) return; return response; };";              
      var result= _driver.ExecutePhantomJS(phantomScript);
      _driver.Navigate().GoToUrl("http://hovertree.com/");
}

     然而運行之後才發現,result的值始終是null,為啥?原來我們忘記了onResourceReceived 是個回調函數,代碼運行到page.onResourceReceived = function(response) {if (response.stage !== \"end\" || response.url != url) return; return response; };的時候就直接返回了。所以,此路不通,為之奈何?看來又只能曲線救國了。看看下面這段代碼:

using (var _driver = new PhantomJSDriver())
{
      const string phantomScript =
            "var url =  'http://hovertree.com/';" +
            "var page = this; page.onResourceReceived = function(response) {if (response.stage !== \"end\" || response.url != url) return; page.tag = response;};";    
                
       _driver.ExecutePhantomJS(phantomScript);
       _driver.Navigate().GoToUrl("http://hovertree.com/");
       var result = (Dictionary<string, object>) _driver.ExecutePhantomJS("var page = this; return page.tag;");
       Console.WriteLine(result["status"]);

}

      JavaScript是動態語言,我們就可以利用這一點,給page動態的創建一個tag屬性,然後把response賦值過去,在頁面加載完成後把tag取回來,這樣就可以獲取到Http的狀態碼啦。接下來只需要判斷result["status"]的值是不是等於200, 就知道頁面是否正確打開了。

    最後,除了onResourceReceived之外, PhantomJS還提供了很多其他很有用處的API,下一回我們聊聊利用onResourceRequested這個API來進行簡單的資源過濾來實現一個基本的AD Block功能。謝謝大家。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved