最近又开始玩采集了,这次的采集目标很别扭,基本上所有的数据都是通过异步加载到页面的,也就是说通过 HttpWebRequest 这个类进行采集的话,基本上获取不到真实数据了,或者非要使用 HttpWebRequest 也不是不可以,但是就需要对每一个返回的 JSon 数据进行单独抓取,感觉好麻烦啊
于是,决定适用 WebBrowser 进行对返回 JSon进行监控
首先在网上找了几个文章,讲解了如何捕获 xhr 数据,(http://www.jb51.net/article/91419.htm)
;(function () { if (typeof window.CustomEvent === 'function') return false; function CustomEvent ( event, params ) { params = params || { bubbles: false, cancelable: false, detail: undefined }; var evt = document.createEvent('CustomEvent'); evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); return evt; } CustomEvent.prototype = window.Event.prototype; window.CustomEvent = CustomEvent; window._xhrs = window._xhrs||{count:0,result:[],send:0}; })(); ;(function() { function ajaxEventTrigger(event) { var ajaxEvent = new CustomEvent(event, { detail: this }); window.dispatchEvent(ajaxEvent); if (event=='ajaxLoadStart'){ window._xhrs['count']++; } if (event=='ajaxLoadEnd'){ if (/^<[\s\S]*>$/gi.test(this['responseText'])){ alert(this['responseText']) }else{ window._xhrs['result'].push(this['responseText']); } returnCSharp(); } } var oldXHR = window.XMLHttpRequest; function newXHR() { var realXHR = new oldXHR(); realXHR.addEventListener('abort', function() { ajaxEventTrigger.call(this, 'ajaxAbort'); }, false); realXHR.addEventListener('error', function() { ajaxEventTrigger.call(this, 'ajaxError'); }, false); realXHR.addEventListener('load', function() { ajaxEventTrigger.call(this, 'ajaxLoad'); }, false); realXHR.addEventListener('loadstart', function() { ajaxEventTrigger.call(this, 'ajaxLoadStart'); }, false); realXHR.addEventListener('progress', function() { ajaxEventTrigger.call(this, 'ajaxProgress'); }, false); realXHR.addEventListener('timeout', function() { ajaxEventTrigger.call(this, 'ajaxTimeout'); }, false); realXHR.addEventListener('loadend', function() { ajaxEventTrigger.call(this, 'ajaxLoadEnd'); }, false); realXHR.addEventListener('readystatechange', function() { ajaxEventTrigger.call(this, 'ajaxReadyStateChange'); }, false); return realXHR; } window.XMLHttpRequest = newXHR; })(); 这段代码很有意思,它实现的目的是重写一个浏览器内的 XMLHttpRequest 对象,并将该对象追加几个事件监控,我在这个 js 代码中定义了一个方法 returnCSharp,用以在符合条件或者数据加载完毕时将数据返回到 WinForm 中,具体实现可以在网上去搜索一下怎么实现 js 和 c# 通讯,这里就不再讲述了
经过测试这个代码是可以使用的,但是产生了一个很让人郁闷的问题,即:使用 WebBrowser.Navigate 之后,js 即可执行了,我再追加这段 js 时就已经遗漏了部分异步请求的信息,这太恶心了,于是又进行一番查找,发现 http://www.cnblogs.com/eaglet/archive/2008/11/19/1336347.html 这里对 WebBrowser 做了一个超出我知识范畴的重点“WebBrowser 其实是对 ActiveX 控件 SHDocVw 的封装,而这个SHDocVw的很多底层调用WebBrowser控件并没有提供实现,我们需要直接操作 SHDoceVw 控件来实现这些高级调用。”
好吧,看看这个高级调用里都有些什么?非常棒!我们发现了这个!BeforeScriptExecute!
很好,我们在构造函数中写上这些
InitializeComponent(); webBrowser1.ObjectForScripting = this; wb = (SHDocVw.WebBrowser)webBrowser1.ActiveXInstance; wb.BeforeScriptExecute += Wb_BeforeScriptExecute; 在 WebBrowser 加载完成之后,js 运行之前将我们想优先运行的东西运行起来,然后实现完整监控 xhr 数据!
于是,加上js执行的调用
private void Wb_BeforeScriptExecute(object pDispWindow) { string text = webBrowser1.DocumentText; IHTMLDocument2 vDocument = (IHTMLDocument2)webBrowser1.Document.DomDocument; try { vDocument.parentWindow.execScript(script, "javascript"); } catch (Exception ex) { this.Close(); } } 很好,这很方便,这样就可以直接通过 WebBrowser 一次调用来获取完整 json 数据,而不用再使用正则啦、Dom啦进行数据分析,以实现降低我们数据分析的成本
补充:引用中追加了以下两个
Microsoft.mshtml 程序集--扩展,使用 IHTMLDocument2 直接运行 JS 代码
windows/system32/shdocvw.dll 使用 BeforeScriptExecute 事件监控加载进度