最近做爬蟲相關工作,我們平時用HttpWebRequest 比較多,每一個Url都要創建一個HttpWebRequest實例,
而且有些網站驗證比較復雜,在登陸及后續抓取數據的時候,每次請求需要把上次的Cookie傳遞給這次請求。
記得這篇博客(http://www.cnblogs.com/dudu/archive/2013/03/05/httpclient.html)結尾,dudu總結了:
HttpClient最與眾不同的地方是同一個HttpClient實例可以發出多次請求,每次請求是可以是完全不同的URL。
而一個HttpWebRequest實例對應於一個Url的一次請求。這才是HttpClient與HttpWebRequest的最大區別所在。
那么為什么不用HttpClient呢?
源碼地址:https://github.com/zzhi/Spider4Net
本着學習的目的,那我就拿知乎練習一下,看看HttpClient好用否?
1,分析登陸頁:https://www.zhihu.com/#signin
根據上圖設置 DefaultRequestHeaders
HttpClient h = new HttpClient( new HttpClientHandler { //CookieContainer = cookies, AutomaticDecompression = DecompressionMethods.GZip //防止返回的json亂碼 | DecompressionMethods.Deflate }); h.DefaultRequestHeaders.Add("UserAgent", Configs.ChromeAgent); h.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4"); h.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch"); h.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); //1.首頁 var response = await h.GetAsync(index); string content = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { //獲取隱藏的input值 HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(content); var xsdf = DocumentHelper.GetInputValue(doc, "_xsrf");//登錄需要 nameValue["_xsrf"] = xsdf; } else { return null; }
2,分析登陸頁:https://www.zhihu.com/login/phone_num(我這里是手機和密碼登錄):
分析:除了要設置DefaultRequestHeaders,還需獲取_xsrf的值<input type="hidden" name="_xsrf" value="3bc639713d3f8bb899009a7dfa37f9d2"/>,
這里還需注意:1, Content-Type: application/x-www-form-urlencoded; charset=UTF-8 該如何設置? 參考地址:http://ronaldrosiernet.azurewebsites.net/Blog/2013/12/07/posting_urlencoded_key_values_with_httpclient
2, 登錄返回的JSON結果是亂碼,該如何處理? 參考地址:http://stackoverflow.com/questions/9242472/retrieve-json-data-with-httpclient
//2.登陸 h.DefaultRequestHeaders.Clear(); h.DefaultRequestHeaders.Add("UserAgent", Configs.ChromeAgent); h.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); h.DefaultRequestHeaders.Add("Origin", index); h.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4"); h.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch"); h.DefaultRequestHeaders.Add("Accept", "*/*"); //post參數 nameValue["password"] = PassWord; nameValue["captcha_type"] = "cn"; nameValue["remember_me"] = "true"; nameValue["phone_num"] = Phone; StringBuilder sb = new StringBuilder(); foreach (var key in nameValue.AllKeys) { sb.AppendFormat("{0}={1}&", key, nameValue[key]); } var str = sb.ToString().TrimEnd('&'); var request = new HttpRequestMessage(HttpMethod.Post, login); var requestContent = str; request.Content = new StringContent(requestContent, Encoding.UTF8, "application/x-www-form-urlencoded"); response = await h.SendAsync(request); content = await response.Content.ReadAsStringAsync(); var dic = DocumentHelper.JsonToDic(content); if (dic.ContainsKey("msg")) { if (dic["msg"] != "登陸成功")//登錄過於頻繁,請稍等重試;errcode:100030 { Console.WriteLine(dic["msg"]); return null; } }
3. 登錄成功后,后面就由大家隨便折騰了。這里獲取登陸后的首頁信息吧。
//3.抓取首頁 h.DefaultRequestHeaders.Clear(); h.DefaultRequestHeaders.Add("UserAgent", Configs.ChromeAgent); h.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4"); h.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch"); h.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); response = await h.GetAsync(index); content = await response.Content.ReadAsStringAsync();
4,我也不對數據做處理了,看看結果:
上圖是調試狀態下的可視化工具視圖。
源碼地址:https://github.com/zzhi/Spider4Net, 里面也包含了用HttpWebRequest 方式登錄的代碼。
以上就是所有了,如果有時間給大家講講登錄驗證碼的識別,但是這個略麻煩,需要根據具體網站的驗證碼訓練一個驗證碼庫,
復雜的無法識別的驗證碼就只能用打碼兔了,其實也可以自己寫個類似打碼兔的軟件,但需要有人值守,人工識別驗證碼。
這這么點東西,昨晚花費了3小時(9-12),今早寫博客又花費了1小時。