前言:
最近在做中國移動爬蟲的過程中,首先遇到的就是 在某個請求中,有一個名為“WT_PFC"的cookie鍵值是由前端JavaScript生成的,沒有進入到HttpWebResponse中。事實上,C#不會去執行客戶端腳本 ,用到的HttpWebRequest不是一個真正意義上的web瀏覽器,它只會下載它所請求的地址的html信息,它永遠不會去執行JavaScript或者ajax,同時WebClient這個類間接地也是調用了HttpWebRequest來請求的,所以結論是一致的。
但是由於其他的請求的Request需要Sent該Cookie,所以查了很多資料,基本上只能 重新構建 js 算法 或者使用 WebBrowser自動去執行頁面js
IHTMLWindow2 win = (IHTMLWindow2)webBrowser1.Document.Window.DomWindow; string s = @"function confirm() {"; s += @"return true;"; s += @"}"; s += @"function alert() {}"; win.execScript(s, "javascript");
, 但這些都不是最好最快的方法。我采用的是以下的
C# 代碼動態編譯JavaScript代碼的方式得出 JavaScript函數被調用之后的 返回值。
1.Cookie(WT_FPC):
2. 通過HttpWatch查到的生成該cookie的js代碼,我提煉出了生成該Cookie的方法,並按格式寫成如下形式:
public static function GetWT_FPC(){ var $t = "2"; var $u = new Date(); var $v = new Date($u.getTime() + 315360000000); var $w = new Date($u.getTime()); if ($t.length < 10) { var $x = $u.getTime().toString(); for (var i = 2; i <= (32 - $x.length); i++) $t += Math.floor(Math.random() * 16.0).toString(16); $t += $x; }; $t = encodeURIComponent($t); return "WT_FPC=id=" + $t + ":lv=" + $u.getTime().toString() + ":ss=" + $w.getTime().toString() ; };
請注意:請按以上的格式書寫 腳本函數,即加上"public static ". 方法體不用動。
3.JsHelper(動態編譯Js代碼):
我把上面js代碼放到本地"WT_FPC.js"文件中
public static class JsHelper { /// <summary> /// 執行JS方法 /// </summary> /// <param name="methodName">方法名</param> /// <param name="para">參數</param> /// <returns></returns> public static string GetJsMethd(string methodName, object[] para) { string path = AppDomain.CurrentDomain.BaseDirectory + "WT_FPC.js"; string str2 = File.ReadAllText(path); StringBuilder sb = new StringBuilder(); sb.Append("package aa{"); sb.Append(" public class JScript {"); sb.Append(str2); sb.Append("}}"); CompilerParameters parameters = new CompilerParameters(); parameters.GenerateInMemory = true; CodeDomProvider _provider = new Microsoft.JScript.JScriptCodeProvider(); CompilerResults results = _provider.CompileAssemblyFromSource(parameters, sb.ToString()); Assembly assembly = results.CompiledAssembly; Type _evaluateType = assembly.GetType("aa.JScript"); object obj = _evaluateType.InvokeMember("GetWT_FPC", BindingFlags.InvokeMethod, null, null, para); return obj.ToString(); } }
注意:以上的helper代碼如果報錯的話,99%都是由於 Js代碼的問題,即js代碼不規范或者 變量缺少定義之類。
4. C#代碼調用helper獲得執行結果
//設置Cookie"WT_FPC" string wt_fpc = JsHelper.GetJsMethd("GetWT_FPC", null); CookieCollection hcc = new CookieCollection(); Cookie wtcookie = new Cookie() { Expires = DateTime.Now.AddYears(10), Path = "/", Domain = ".10086.cn", Name = "WT_FPC", Value = wt_fpc.Substring(wt_fpc.IndexOf('=') + 1, wt_fpc.Length - 7)// }; hcc.Add(wtcookie); HttpHelperNew.cookie.Add(wtcookie);
5. 小經驗: 有時候 JavaScript前端生成的cookie,有時候 服務器端並不 校驗,也就是,如果把這個cookie值不通過js代碼動態得到,直接 寫死的話 也應該可以。
6. 其他:
一:如果需要用C#執行前端js 函數計算的結果,比如 前端js的加密結果,在能找到js代碼前提下,可以把js寫在本地的html 頁面里面,然后C#代碼去訪問 即可。so easy!
二: Java 中使用WebClient獲取js執行完返回的值。
try{ WebClient webClient = new WebClient(BrowserVersion.FIREFOX_24); //設置webClient的相關參數 webClient.getOptions().setJavaScriptEnabled(true); webClient.getOptions().setCssEnabled(false); webClient.setAjaxController(new NicelyResynchronizingAjaxController()); //webClient.getOptions().setTimeout(50000); webClient.getOptions().setThrowExceptionOnScriptError(false); //模擬瀏覽器打開一個目標網址 HtmlPage rootPage= webClient.getPage(url); System.out.println("為了獲取js執行的數據 線程開始沉睡等待"); Thread.sleep(3000);//主要是這個線程的等待 因為js加載也是需要時間的 System.out.println("線程結束沉睡"); String html = rootPage.asText(); System.out.println(html); }catch(Exception e){ }