說起采集,其實我是個外行,以前拔過阿里巴巴的客戶數據,在我博客的文章:C#+HtmlAgilityPack+XPath帶你采集數據(以采集天氣數據為例子) 中,介紹過采集用的工具,其實很Low的,分析Html,用開源的HtmlAgilityPack就很快解決問題了。我個人並不是技術特別深,所以只要是解決問題就OK了。但每一次需求並不是完全一致的,對上面那篇文章的采集,無需登錄,是非常靈活的,但是這次碰到的稍微有點變態,雖然最后任務完成,但總結方案還是很low的,但覺得還是有必要分享出來,希望對以后碰到這個問題的人有用。
1.采集目目標特點與分析
由於采集目標比較商業性,不便於透露。所以文字多一點。大家仔細看。
目標網站特點以及初始需求:
1) 要求登錄,並且登錄安全驗證非常嚴,怎么嚴就不說了,就是非常困難,動不動還得手機驗證。
2) 網站數據經過分析,都是直接登錄后通過https請求來獲取JSON數據,解析就可以用,但是里面也有變態的地方,就是有些參數是加密的,直接扒歷史數據有困難;
3) 初期網站每天需要采集的URL類型超過60+左右,但總的采集鏈接數超過2000+,因為一個類型的URL,有很多不同的區域的數據需要采集;
最需要說明的是該項目所有的頁面分析,解析和采集入庫工作由2個畢業生來做,我也沒時間去做很深入的研究,他們C#語法還不是很熟練,所以技術方案上不能太復雜,越簡單越好。
先看看我這方案的過程,由於工作太多,剛開始想法是很好的,但是越做到后面,發現越麻煩。
2.方案第一版-Low到爆,別笑話
該采集最大的難點就是登錄的問題,由於安全性太高,所以一開始我就徹底放棄了程序模擬登錄的思路,雖然后面的過程有點曲折,但這也是新人很快能搞定所有采集的關鍵。否則在前面就不知道要花費多少時間。說說初步的想法:
1.既然登錄后就可以采集數據,那就人工登錄一次,做一個asp.net web頁面,點擊開始,就可以使用WebClient請求JSON數據。因為在同一個瀏覽器,登錄的session啥都可以共享,我是這么想的。
2.請求數據后直接解析到數據庫;
3.大量的URL多線程執行應該速度很不錯,So,沒啥壓力;
大神一眼都能看出問題哈,想法很美好,現實是很殘酷。
3.碰壁后的第二版方案
基本JSON的解析工作差不多讓新人完成后,自己做了個asp.net 的web測試,真是xxx,獲取不到JSON數據,一調試,才知道,WebClient請求這樣搞是不行的,在web頁面直接請求url也是不行的,就是所謂的跨域問題,雖然沒搞過前端,但也能理解。好吧,這么low的問題,真的不好意思拿出來說,誰叫沒動手試一下呢。那怎么破?所以有了第二版方案:
1.既然不能跨域,那就讓你跨。在大石頭和邱哥的提示下,用webbrowse來搞吧。
2.使用webbrowse控件手動登錄后,直接在控件中請求新的URL,獲取JSON值;對,這樣看你跨不跨過去。。。測試一個鏈接也是可行的。
然后發現真的是 萬事開頭難,中間難,結尾更難。。。想法很美好,現實是很殘酷!
4.最終方案第三版
4.1 該死的completed事件
按照方案2,很快所有的鏈接加進去了,開始采集,又杯具了。只有第一個鏈接能執行。。。調試,很快發現問題,也怪學藝不精,以前沒用過webbrowse啊:
webbrowse請求url后,是不能馬上獲取到請求的html文本的,要在completed事件中處理獲取到的html文本(JSON字符串)才行。怎么破?
既然要用事件,那也挺好,不斷請求,不斷解析,把處理邏輯加到事件中就好了。很OK,繼續進行中。。。
4.2 沒想到URL請求太快
沒跑幾個URL,xxx,問題又來了:當初由於采集的頁面和鏈接太多,所以做了幾個菜單,點擊后分別采集不同的任務,但是點擊后這個請求不斷的發,這個事件執行是有問題的,很多沒執行到,有一些中間變量值被覆蓋了,請求太快啊。。。怎么破?那我就設置個長一點的時間,確保每次加載並處理完成才去請求下一次。好,說干就干,看了一下每個頁面的解析,10秒足夠了,那就搞10秒請求一次。。。。繼續進行中
還是xxx,過幾天這個網站改版了,改動很多,很無奈啊,但生活還得繼續。還好我們的解析都是利用工具生成的東西,改起來很快。具體可以參考這篇文章:C#+HtmlAgilityPack+XPath帶你采集數據(以采集天氣數據為例子)。
改版的一個主要變化是URL類型增加了很多,所以數據也增加了很多,以前是總共也就50-60個類型的鏈接需要采集,但是改版后,上升到2000+,增加了很多子類型的數據,你懂的。然后發現這個10秒一次,啥時候能采集完成啊。。。而且很明顯,10秒對很多URL來說多很多,不是白白浪費嗎。。。好吧,知道問題,那就想辦法解決,如果連問題都不知道,那就杯具了。
都搞10秒不公平,那就搞個timer吧,定時執行,但是在請求之前和解析之后,動態修改這個timer的間隔執行時間,一秒也不浪費啊。比如請求之前設置間隔是10秒,事件里面解析之后設置為1毫秒。。。哈哈,So easy 。。。。
4.3 想開得和飛機一樣快,但火車的速度都沒到
xxx,跑了上百個之后,發現還是太慢,任務太多啊。。。每一個好幾秒也hold不足。。。。那怎么破,分析一下原因,很明顯請求加載速度很快,慢在處理那里,主要是事件處理那里不好加多線程。那怎么破,辦法總比問題多。。。怕啥。。。
反正就是每天采集下,那弄個中間緩存唄。當然本地緩存也是可以的啊,這里特意用了一個windows版的redis做了個測試,目的是有2個,因為我們組的項目中redis用的比較廣泛,新的畢業生來雖然沒玩過,但我們日常也在說,以后項目也會用到,所以特意在這個地方教他們使用了一下,比使用Oracle當然更簡單,平時他們都是用Oracle,所以理解起來也不費勁。所以新的方案又來了:
使用timer來控制發送請求的間隔,在完成事件中,把json值和相關解析要用到的變量緩存到redis中,然后開一個多線程從redis里面取值進行解析工作。。。所干就干,用了30行代碼改造了一下,很快就測試。。。不得不說,Paralller.For好用啊,請求的速度提高到60-80個/秒,速度很快了,解析4個線程開起來,速度也杠桿的,沒計算具體的時間,但是解析完也就比請求完多個幾分鍾。開發機安裝東西太多,而且CPU比較雞肋,所以開4個線程已經100%了,所以這個效率也夠了。至此,整個系統的核心工作讓2個新人折騰來折騰去,給他們思路,和簡單的示例代碼,就搞定了。
5.總結
當然過程的細節還有很多要注意,特別是解析工作,在前一篇文章中說過了。
其實從分析頁面鏈接,到解析,到最后數據入庫,代碼給他們過指導,但大部分工作是新人完成的。這個過程讓他們也對項目和數據有了很深的了解,自己也會更輕松一些,畢竟從頭接觸,出了問題,他們可以排查。在從頭到尾的過程中,還有很多細節,他們自己也排查和發現了很多bug,但總歸要給他們試錯的機會,能改正就好。在總體方案的變化下,從解決碰到的問題,要簡單的優化,多線程,redis使用,都有了直觀的了解和認識(為什么要用,什么時候要用,為什么一開始不考慮?),多幾行代碼,速度瞬間提升。。。
解決問題的方法比問題要多,思路決定出路,用簡單的方法解決問題就OK!
快速發現問題並能有解決方案是很重要的一個方面。
方案整體代碼不能提供,不過從百度拔的一些公共代碼都在上面了,以及前面一篇文章中有介紹。其他都是細節問題,主要是這個方案過程比較曲折一點點。
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!歡迎各位轉載,但是未經作者本人同意,轉載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利。