如何在使用 RemoteWebDriver 打开网页的同时获取 Http 状态码
最近一直在用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 状态码的更多相关文章
- C#带参数打开网页及url获取
1.带参数打开网页 Response.Redirect("form2.aspx?id=url1&name=ok"); 其中?后面为参数. 2.获取url 命令 结果 Req ...
- 网页中常见返回HTTP状态码含义
在日常网页浏览的过程中大家经常会碰到400,403,404,500,502等HTTP状态码,这些状态码对于一般用户来说出现什么都是一样的,反正就是页面打不开了,但是作为网站开发人员或者从事相关工作者认 ...
- Android -- 真正的 高仿微信 打开网页的进度条效果
(本博客为原创,http://www.cnblogs.com/linguanh/) 目录: 一,为什么说是真正的高仿? 二,为什么要搞缓慢效果? 三,我的实现思路 四,代码,内含注释 五,使用方法与截 ...
- 在WPF程序中打开网页:使用代理服务器并可进行JS交互
本项目环境:使用VS2010(C#)编写的WPF程序,通过CefSharp在程序的窗体中打开网页.需要能够实现网页后台JS代码中调用的方法,从网页接收数据,并能返回数据给网页.运行程序的电脑不允许上网 ...
- 使用urllib2打开网页的三种方法
#coding:utf-8 import urllib2 import cookielib url="http://www.baidu.com" print '方法 1' resp ...
- Apache 打开网页的时候等待时间过长的解决方案
服务器搭建后经常在打开页面的时候,等待很长时间,有时候,都超过一分钟了,然后才能打开,但是打开后,速度又很快,休息一会再点击,又会很慢了,遇到了这种问题很头疼,由于不是专业做服务器配置的,所以刚开始没 ...
- (转)C#调用默认浏览器打开网页的几种方法
转载,原文地址:http://blog.csdn.net/testcs_dn/article/details/42246969 CSharp调用默认浏览器打开网页的几种方法 示例界面: 方法一:从注册 ...
- 用webBrowser打开网页出现脚本错误怎么办
当IE浏览器遇到脚本错误时,在浏览器左下角会出现一个黄色图标,点击可以查看脚本错误的详细信息,并不会有弹出的错误信息框.我们在用webBrowser编写的程序打开网页,遇到脚本有问题是,会弹出一个错误 ...
- js实现打开网页自动弹出添加QQ好友邀请窗口
我们有时进一些网面或专题页面会自动弹出一个加为好友的对话框了,在研究了很久之后发现可以直接使用js来实现,下面我们一起来看js实现打开网页自动弹出添加QQ好友邀请窗口的方法. 第一步.JS脚本 这个是 ...
随机推荐
- json换行符的处理
JS端的: var s = JSON.stringify(str); var ss = s.replace(/\\n/g, "\\n") .replace(/\\'/g, &quo ...
- android学习笔记38——样式和主题
Style.Theme 样式和主题资源都是用于android应用的美化操作. 样式:一组格式的集合,可重复使用. android的样式资源存放与res/values文件夹下,其根元素为<reso ...
- android学习笔记四
TextView.Button.CheckBox.RadoiButton.EditView.ImageButton.ToogleButton——略 AnalogClock.DigitalClock = ...
- 异常处理:Sys.WebForms.PageRequestManagerParserErrorException:The message……
如果你为了使页面可以达到局部刷新的效果,并且用了UpdatePanel控件,这是如果你在后台页面用到Response对象时肯呢过会抛出一下异常: 解决方法:$(document).ready(func ...
- 【转】一道SQL SERVER DateTime的试题
学习过上一篇SQL SERVER DateTime精度的文章后.再来做一道题. IF ('2011-07-31 00:00:00.000' BETWEEN '2011-07-01' and '2011 ...
- Mysql存储过程简明使用
mysql> \d // 改变命令行下的结束符标志mysql> create procedure p3() -> begin -> set @i=1; # 这样也可以定义变 ...
- [Android NDK/JNI-1A]-开发环境搭建
NDK简介 NDK能干什么:NDK使得在android中,java可以调用C 函数库. 1.Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意 ...
- studio adb连接不上手机 ADB server didn't ACK
问题描述:在eclipse的Logcat出现错误 [2014-01-08 14:00:07 - adb] ADB server didn't ACK [2014-01-08 14:00:07 - ad ...
- MSSQL死锁产生原因及解决方法
一. 什么是死锁 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等的进 ...
- Yii 框架表单验证---实例