最近一直在用Selenium这个开源项目写一些web 自动化的小玩意。本来一直运行的挺好,直到有一天突然发现资源抓取失败了,翻看日志才发现,原来本该正常打开的页面返回了504错误所以自然失败了。如何避免这种情况呢?事实上对于Selenium提供的RemoteWebDriver 来说,一般都是采用下面两种方式来打开网页:

 using (var _driver = new PhantomJSDriver())
{
_driver.Navigate().GoToUrl("http://www.cnblogs.com/");
//或
_driver.Url = "http://www.cnblogs.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://www.cnblogs.com/");
}

我们把每一个获取到的response都打印出来看看,还真不少。注意看,有一些response出现了两遍, 对应的response.stage分别是start和end,这是指什么呢?官网上也给出了解释:stage : "start", "end" (FIXME: other value for intermediate chunk?)

当然对于我们来说,不需要这么多,只需要知道http://www.cnblogs.com/这个URL本身的状态,因此我们简化一下上面的代码, 过滤到状态不是end和URL不是http://www.cnblogs.com/的内容。

using (var _driver = new PhantomJSDriver())
{
const string phantomScript =
"var url = 'http://www.cnblogs.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://www.cnblogs.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://www.cnblogs.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://www.cnblogs.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://www.cnblogs.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://www.cnblogs.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功能。谢谢大家。

如何在使用 RemoteWebDriver 打开网页的同时获取 Http 状态码的更多相关文章

  1. C#带参数打开网页及url获取

    1.带参数打开网页 Response.Redirect("form2.aspx?id=url1&name=ok"); 其中?后面为参数. 2.获取url 命令 结果 Req ...

  2. 网页中常见返回HTTP状态码含义

    在日常网页浏览的过程中大家经常会碰到400,403,404,500,502等HTTP状态码,这些状态码对于一般用户来说出现什么都是一样的,反正就是页面打不开了,但是作为网站开发人员或者从事相关工作者认 ...

  3. Android -- 真正的 高仿微信 打开网页的进度条效果

    (本博客为原创,http://www.cnblogs.com/linguanh/) 目录: 一,为什么说是真正的高仿? 二,为什么要搞缓慢效果? 三,我的实现思路 四,代码,内含注释 五,使用方法与截 ...

  4. 在WPF程序中打开网页:使用代理服务器并可进行JS交互

    本项目环境:使用VS2010(C#)编写的WPF程序,通过CefSharp在程序的窗体中打开网页.需要能够实现网页后台JS代码中调用的方法,从网页接收数据,并能返回数据给网页.运行程序的电脑不允许上网 ...

  5. 使用urllib2打开网页的三种方法

    #coding:utf-8 import urllib2 import cookielib url="http://www.baidu.com" print '方法 1' resp ...

  6. Apache 打开网页的时候等待时间过长的解决方案

    服务器搭建后经常在打开页面的时候,等待很长时间,有时候,都超过一分钟了,然后才能打开,但是打开后,速度又很快,休息一会再点击,又会很慢了,遇到了这种问题很头疼,由于不是专业做服务器配置的,所以刚开始没 ...

  7. (转)C#调用默认浏览器打开网页的几种方法

    转载,原文地址:http://blog.csdn.net/testcs_dn/article/details/42246969 CSharp调用默认浏览器打开网页的几种方法 示例界面: 方法一:从注册 ...

  8. 用webBrowser打开网页出现脚本错误怎么办

    当IE浏览器遇到脚本错误时,在浏览器左下角会出现一个黄色图标,点击可以查看脚本错误的详细信息,并不会有弹出的错误信息框.我们在用webBrowser编写的程序打开网页,遇到脚本有问题是,会弹出一个错误 ...

  9. js实现打开网页自动弹出添加QQ好友邀请窗口

    我们有时进一些网面或专题页面会自动弹出一个加为好友的对话框了,在研究了很久之后发现可以直接使用js来实现,下面我们一起来看js实现打开网页自动弹出添加QQ好友邀请窗口的方法. 第一步.JS脚本 这个是 ...

随机推荐

  1. Android开发——通过扫描二维码,打开或者下载Android应用

    Android开发——通过扫描二维码,打开或者下载Android应用   在实现这个功能的时候,被不同的浏览器折磨的胃疼,最后实现了勉强能用,也查考了一下其他人的博客 android实现通过浏览器点击 ...

  2. 黄聪:PHP json_encode中文乱码解决方法

    相信很多人在使用Ajax与后台php页面进行交互的时候都碰到过中文乱码的问题.JSON作为一种轻量级的数据交换格式,备受亲睐,但是用PHP作为后台交互,容易出现中文乱码的问题.JSON和js一样,对于 ...

  3. 关于Cannot assign to 'self' outside of a method in the init family解决方法

    有时候我们重写父类的init方法时不注意将init后面的第一个字母写成了小写,在这个方法里面又调用父类的初始化方法(self = [super init];)时会报错,错误信息如下:error:Can ...

  4. DBA_Oracle冷备份案例脚本本法(案例)

    2014-08-10 Created By BaoXinjian

  5. OAF_EO系列4 - Create详解和实现(案例)

    2014-06-02 Created By BaoXinjian

  6. cf519D . A and B and Interesting Substrings 数据结构map

    题意: 已知26个小写字母有各自的权值(正,负,或0) 现在给出一个字符串,长度<=1e5 问这个字符串有多少个子串满足: 开头的字母和结尾的字母一样 字符串除了开头和结尾的字母外,其余的字母的 ...

  7. Git Step by Step

    原文地址:http://www.cnblogs.com/wilber2013/category/643754.html 1.Git简介 2.Git本地仓库 3.Git对象模型 4.探索.git目录 5 ...

  8. 转-Fragment+FragmentTabHost组件(实现新浪微博底部菜单)

    http://www.cnblogs.com/lichenwei/p/3985121.html 记得之前写过2篇关于底部菜单的实现,由于使用的是过时的TabHost类,虽然一样可以实现我们想要的效果, ...

  9. linux中deb怎样安装

    deb是Debian Linux的安装格式,跟Red Hat的rpm非常相似,最基本的安装命令是:dpkg -i file.debdpkg 是Debian Package的简写,是为Debian 专门 ...

  10. struts2标签获取parameter,request,session,application中的值

    http://localhost:8080/demo/index.jsp?flag=kkkk <s:property value="#parameters.flag" /&g ...