有了上一篇的介紹,此次咱們來講說模擬登陸,上一篇見 :c# 爬蟲(一) HELLO WORLDhtml
原理web
咱們知道,通常須要登陸的網站,服務器和客戶端都會有一段時間的會話保持,而這個會話保持是在登陸時候創建的, 服務端和客戶端都會持有這個KEY,在後續訪問時,都須要覈對這兩個KEY是否一致。 而客戶端的這個KEY就存在cookie中。 所以,咱們須要獲取登陸後的cookie值,並在後續的訪問中,都添加這個cookie。這樣才能作到模擬登陸的效果。chrome
例子:c#
咱們以獲取博客園首頁的園齡爲例。須要作三步服務器
1. 模擬登陸博客園cookie
2. 構建我的主頁的Request請求,包括cookiesession
3. 獲取我的主頁的數據後, 分析頁面,並獲取園齡的數據。app
代碼以下:工具
static void Main(string[] args) { //string html= Hello(); string html = LoginSimulation(); Console.WriteLine(html); Console.Read(); } static string LoginSimulation() { string url = "https://passport.cnblogs.com/user/signin"; string postData = "{\"input1\":\"MvxmwEWfUF26IvKNa1dUiZn1xmSBhNW0wJyoaUlDPXoh+Mb+z2eZK3r3c9Jd0aT0/Wzz3ht7LMeTllu8ISY9nfQIuKB0C19Y9/IfKYSktpZZOVaKx/XP3i/mGxXC3K5m2la91ViRh3BO36xT4E98dbqVHPtynjuNafuVIBF5a2M=\",\"input2\":\"xxxx":false}"; //1.獲取登陸Cookie HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); req.Method = "POST";// POST OR GET, 若是是GET, 則沒有第二步傳參,直接第三步,獲取服務端返回的數據 req.AllowAutoRedirect = false;//服務端重定向。通常設置false req.ContentType = "application/x-www-form-urlencoded";//數據通常設置這個值,除非是文件上傳 byte[] postBytes = Encoding.UTF8.GetBytes(postData); req.ContentLength = postBytes.Length; Stream postDataStream = req.GetRequestStream(); postDataStream.Write(postBytes, 0, postBytes.Length); postDataStream.Close(); HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); string cookies = resp.Headers.Get("Set-Cookie");//獲取登陸後的cookie值。 //2.登陸想爬取頁面的構造,主要多一個Cookie的構造 string contentUrl = "https://home.cnblogs.com/u/xinjian/"; HttpWebRequest reqContent = (HttpWebRequest)WebRequest.Create(contentUrl); reqContent.Method = "GET"; reqContent.AllowAutoRedirect = false;//服務端重定向。通常設置false reqContent.ContentType = "application/x-www-form-urlencoded";//數據通常設置這個值,除非是文件上傳 reqContent.CookieContainer = new CookieContainer(); reqContent.CookieContainer.SetCookies(reqContent.RequestUri, cookies);//將登陸的cookie值賦予這次的請求。 HttpWebResponse respContent = (HttpWebResponse)reqContent.GetResponse(); string html = new StreamReader(respContent.GetResponseStream()).ReadToEnd(); //3. 分析讀取該頁面的數據,可使用HtmlAgilityPack第三方類,這裏比較簡單,本身寫個獲取方法就行 string age= GetVal(html, "<span title='入園時間:2010-6-28'>", "</span>"); return age; }
注意事項post
1. 本次模擬登陸,我發現chrome的開發人員工具,並無抓到真正的Post包,和我以前遇到的狀況同樣, 後來仍是使用了httpwatch後,才抓到了真正的數據包。博客園作的不錯,提交的數據進行了加密。 固然個人密碼我也已經改爲了XXX,用戶須要運行的話,須要自行抓包獲取對應的postData。
2. 針對cookie的賦值,主要由這兩句完成
reqContent.CookieContainer = new CookieContainer(); reqContent.CookieContainer.SetCookies(reqContent.RequestUri, cookies);//將登陸的cookie值賦予這次的請求。
可是據說C#封裝的不是很好,有時候會漏掉數據,但我目前還沒遇到過, 若是遇到,須要將cookie的string手工轉換成CookieCollection,並賦予CookieContainer。
3. 針對ASP.NET的網頁, 會存在__VIEWSTATE & __EVENTVALIDATION 這兩個post字段, 對於沒創建會話時,這兩個值是不會變的,而一旦創建會話(模擬登錄後),每次訪問的頁面,這兩個值都會改變, 解決辦法是先使用GET獲取該頁面的數據後,獲取這兩個字段的值, 而後在post的時候,進行賦值。
4. 在遇到500錯誤的時候,說實話,我也很頭疼,不知道如何調試,但我總結下來,必定是request構建的不對。主要查看以下問題:
4.1. 對比post的數據的key和value,看看格式是否正確,如是否進行了編碼 WebUtility.UrlEncode()。
4.2. 對比post的數據的,是否Post了所有的數據, 固然這裏不光是當前頁面,有時候還會用到其餘頁面,我舉個例子, 我在訂單頁面上傳附件, 在附件上傳頁面,發現並無Post訂單的id,那麼這個時候,就須要查找訂單的id服務端是何時獲取的,這個時候就須要猜了,有多是在打開訂單頁面的時候,服務端就把此id存儲到session中了。因此先須要模擬打開訂單頁面,而後在模擬訂單附件上傳的post。
4.3 注意是是否犯了__VIEWSTATE & __EVENTVALIDATION的錯誤,注意,針對數據型的post,須要進行urlEncode。
4.4 Request的Head是否構建全了, 有時候客戶端會提交自定義的head,注意查看。同時UserAgent有時候也會須要進行變化,但目前我還沒遇到過。
4.5. 若是肯定Post的數據全了,而且仍是500錯誤的話,考慮下是否cookie有問題,雖然我還沒遇到過。
目前就想到這麼多,模擬登陸當時我也卡了兩天,主要當時對__VIEWSTATE & __EVENTVALIDATION的理解仍是不夠,後續若是有疑問的話,我會專門開一篇ASP.NET的模擬登陸。
參考文獻:
http://www.crifan.com/emulate_login_website_using_csharp/
http://www.cnblogs.com/zuoguanglin/archive/2012/03/28/2421153.html
https://msdn.microsoft.com/en-us/library/ms972976.aspx