大規模數據採集流程和思路

爬蟲是一個比較容易上手的技術,也許花5分鐘看一篇文檔就能爬取單個網頁上的數據。但對於大規模爬蟲,完全就是另一回事,並不是1*n這麼簡單,還會衍生出許多別的問題。

系統的大規模爬蟲流程如圖所示。

大規模爬蟲流程總結

先檢查是否有API

API是網站官方提供的數據接口,如果通過調用API採集數據,則相當於在網站允許的範圍內採集,這樣既不會有道德法律風險,也沒有網站故意設置的障礙;不過調用API接口的訪問則處於網站的控制中,網站可以用來收費,可以用來限制訪問上限等。整體來看,如果數據採集的需求並不是很獨特,那麼有API則應優先採用調用API的方式。

 

數據結構分析和數據存儲

  • 爬蟲需求要十分清晰,具體表現爲需要哪些字段,這些字段可以是網頁上現有的,也可以是根據網頁上現有的字段進一步計算的,這些字段如何構建表,多張表如何連接等。值得一提的是,確定字段環節,不要只看少量的網頁,因爲單個網頁可以缺少別的同類網頁的字段,這既有可能是由於網站的問題,也可能是用戶行爲的差異,只有多觀察一些網頁才能綜合抽象出具有普適性的關鍵字段——這並不是幾分鐘看幾個網頁就可以決定的簡單事情,如果遇上了那種臃腫、混亂的網站,可能坑非常多。
  • 對於大規模爬蟲,除了本身要採集的數據外,其他重要的中間數據(比如頁面Id或者url)也建議存儲下來,這樣可以不必每次重新爬取id。
  • 數據庫並沒有固定的選擇,本質仍是將Python裏的數據寫到庫裏,可以選擇關係型數據庫MySQL等,也可以選擇非關係型數據庫MongoDB等;對於普通的結構化數據一般存在關係型數據庫即可。sqlalchemy是一個成熟好用的數據庫連接框架,其引擎可與Pandas配套使用,把數據處理和數據存儲連接起來,一氣呵成。

 

數據流分析

  • 對於要批量爬取的網頁,往上一層,看它的入口在哪裏;這個是根據採集範圍來確定入口,比如若只想爬一個地區的數據,那從該地區的主頁切入即可;但若想爬全國數據,則應更往上一層,從全國的入口切入。一般的網站網頁都以樹狀結構爲主,找到切入點作爲根節點一層層往裏進入即可。
  • 值得注意的一點是,一般網站都不會直接把全量的數據做成列表給你一頁頁往下翻直到遍歷完數據,比如鏈家上面很清楚地寫着有24587套二手房,但是它只給100頁,每頁30個,如果直接這麼切入只能訪問3000個,遠遠低於真實數據量;因此先切片,再整合的數據思維可以獲得更大的數據量。顯然100頁是系統設定,只要超過300個就只顯示100頁,因此可以通過其他的篩選條件不斷細分,只到篩選結果小於等於300頁就表示該條件下沒有缺漏;最後把各種條件下的篩選結果集合在一起,就能夠儘可能地還原真實數據量。
  • 明確了大規模爬蟲的數據流動機制,下一步就是針對單個網頁進行解析,然後把這個模式複製到整體。對於單個網頁,採用抓包工具可以查看它的請求方式,是get還是post,有沒有提交表單,欲採集的數據是寫入源代碼裏還是通過AJAX調用JSON數據。
  • 同樣的道理,不能只看一個頁面,要觀察多個頁面,因爲批量爬蟲要弄清這些大量頁面url以及參數的規律,以便可以自動構造;有的網站的url以及關鍵參數是加密的,這樣就悲劇了,不能靠着明顯的邏輯直接構造,這種情況下要批量爬蟲,要麼找到它加密的js代碼,在爬蟲代碼上加入從明文到密碼的加密過程;要麼採用下文所述的模擬瀏覽器的方式。

 

數據採集

  • 之前用R做爬蟲,不要笑,R的確可以做爬蟲工作;但在爬蟲方面,Python顯然優勢更明顯,受衆更廣,這得益於其成熟的爬蟲框架,以及其他的在計算機系統上更好的性能。scrapy是一個成熟的爬蟲框架,直接往裏套用就好,比較適合新手學習;requests是一個比原生的urllib包更簡潔強大的包,適合作定製化的爬蟲功能。requests主要提供一個基本訪問功能,把網頁的源代碼給download下來。一般而言,只要加上跟瀏覽器同樣的Requests Headers參數,就可以正常訪問,status_code爲200,併成功得到網頁源代碼;但是也有某些反爬蟲較爲嚴格的網站,這麼直接訪問會被禁止;或者說status爲200也不會返回正常的網頁源碼,而是要求寫驗證碼的js腳本等。
  • 下載到了源碼之後,如果數據就在源碼中,這種情況是最簡單的,這就表示已經成功獲取到了數據,剩下的無非就是數據提取、清洗、入庫。但若網頁上有,然而源代碼裏沒有的,就表示數據寫在其他地方,一般而言是通過AJAX異步加載JSON數據,從XHR中找即可找到;如果這樣還找不到,那就需要去解析js腳本了。

 

解析工具

源碼下載後,就是解析數據了,常用的有兩種方法,一種是用BeautifulSoup對樹狀HTML進行解析,另一種是通過正則表達式從文本中抽取數據。

  • BeautifulSoup比較簡單,支持XpathCSSSelector兩種途徑,而且像Chrome這類瀏覽器一般都已經把各個結點的Xpath或者CSSSelector標記好了,直接複製即可。以CSSSelector爲例,可以選擇tag、id、class等多種方式進行定位選擇,如果有id建議選id,因爲根據HTML語法,一個id只能綁定一個標籤。
  • 正則表達式很強大,但構造起來有點複雜,需要專門去學習。因爲下載下來的源碼格式就是字符串,所以正則表達式可以大顯身手,而且處理速度很快。

對於HTML結構固定,即同樣的字段處tag、id和class名稱都相同,採用BeautifulSoup解析是一種簡單高效的方案,但有的網站混亂,同樣的數據在不同頁面間HTML結構不同,這種情況下BeautifulSoup就不太好使;如果數據本身格式固定,則用正則表達式更方便。比如以下的例子,這兩個都是深圳地區某個地方的經度,但一個頁面的class是long,一個頁面的class是longitude,根據class來選擇就沒辦法同時滿足2個,但只要注意到深圳地區的經度都是介於113到114之間的浮點數,就可以通過正則表達式"11[3-4].\d+"來使兩個都滿足。

 

數據整理

一般而言,爬下來的原始數據都不是清潔的,所以在入庫前要先整理;由於大部分都是字符串,所以主要也就是字符串的處理方式了。

  • 字符串自帶的方法可以滿足大部分簡單的處理需求,比如strip可以去掉首尾不需要的字符或者換行符等,replace可以將指定部分替換成需要的部分,split可以在指定部分分割然後截取一部分。
  • 如果字符串處理的需求太複雜以致常規的字符串處理方法不好解決,那就要請出正則表達式這個大殺器。
  • Pandas是Python中常用的數據處理模塊,雖然作爲一個從R轉過來的人一直覺得這個模仿R的包實在是太難用了。Pandas不僅可以進行向量化處理、篩選、分組、計算,還能夠整合成DataFrame,將採集的數據整合成一張表,呈現最終的存儲效果。

 

寫入數據庫

如果只是中小規模的爬蟲,可以把最後的爬蟲結果匯合成一張表,最後導出成一張表格以便後續使用;但對於表數量多、單張表容量大的大規模爬蟲,再導出成一堆零散的表就不合適了,肯定還是要放在數據庫中,既方便存儲,也方便進一步整理。

  • 寫入數據庫有兩種方法,一種是通過Pandas的DataFrame自帶的to_sql方法,好處是自動建表,對於對錶結構沒有嚴格要求的情況下可以採用這種方式,不過值得一提的是,如果是多行的DataFrame可以直接插入不加索引,但若只有一行就要加索引否則報錯,雖然這個認爲不太合理;另一種是利用數據庫引擎來執行SQL語句,這種情況下要先自己建表,雖然多了一步,但是表結構完全是自己控制之下。Pandas與SQL都可以用來建表、整理數據,結合起來使用效率更高。
  • 寫入數據庫有兩種思路,一種是等所有的數據都爬完,集中一次向量化清洗,一次性入庫;另一種是爬一次數據清洗一次就入庫。表面上看前者效率更高,但是對於大規模爬蟲,穩定性也是要考慮的重要因素,因爲在長久的爬蟲過程中,總不可避免會出現一些網絡錯誤,甚至如果出現斷網斷電的情況,第一種情況下就全白費了,第二種情況下至少已入庫的不會受影響,並且單次的清洗和入庫是很快的,基本不怎麼費時間,所以整體來看推薦第二種思路。

 

爬蟲效率提升

對於大規模爬蟲,效率是一個核心問題。單個網頁爬取可能很大,一旦網頁數量級大增之後,任務量也會大增,同時方式下的耗時也會大增。沒有公司或人個願意爬個幾十萬上百萬的頁面還要等幾個月,因此優化流程、提高效率是非常必要的。

  • 儘量減少訪問次數。單次爬蟲的主要耗時在於網絡請求等待響應,所以能減少訪問就少訪問,既減少自己的工作量,也減輕網站的壓力,還降低被封的風險。首先要做的就是流程優化,儘可能精簡流程,一些數據如果可以在一個頁面內獲取而不必非要在多個頁面下獲取,那就只在一個頁面內獲取。然後去重也是非常重要的手段——網站並不是嚴格意義的互不交叉的樹狀結構,而是多重交叉的網狀結構,所以從多個入口深入的網頁會有很多重複,一般根據url或者id進行唯一性判別,爬過的就不再繼續爬了。最後,值得深思的一點就是,是不是所有的數據都需要爬?對於那些響應慢,反爬機制很嚴格的網站,爬少量的都困難,爬大量的時間成本就會高到難以接受,這種情況下怎麼辦?舉一個例子,對於氣象數據,已知的一點是時間、空間越接近的地方數據就越接近,那麼你爬了一個點的氣象數據之後,100米以內的另一個點就可以不用再爬,因爲可預期一定是跟之前的點差不多;這個時候就可以採用機器學習的方法,爬取一部分數據作爲訓練數據,其他的進行預測,當對數據的準確性要求不是特別高,當模型的性能比較好,採用機器學習模型預測就可以省下大部分爬蟲的工作。雖然專業的爬蟲工程師懂機器學習的可能不多,但這正是複合型人才的優勢。
  • 大量爬蟲是一個IO阻塞的任務,因此採用多進程、多線程或者協程的併發方式可以有效地提高整理速度。個人推薦用協程,速度比較快,穩定性也比較好。
  • 即使把各種法子都用盡了,單機單位時間內能爬的網頁數仍是有限的,面對大量的頁面隊列,可計算的時間仍是很長,這種時候就必須要用機器換時間了,這就是分佈式爬蟲。首先,分佈式不是爬蟲的本質,也不是必須的,對於互相獨立、不存在通信的任務就可手動對任務分割,然後在多臺機器上分別執行,減少每臺機器的工作量,耗時就會成倍減少。比如有100W個頁面待爬,可以用5臺機器分別爬互不重複的20W個頁面,相對單機耗時就縮短了5倍。但是如果存在着需要通信的狀況,比如一個變動的待爬隊列,每爬一次這個隊列就會發生變化,即使分割任務也就有交叉重複,因爲各個機器在程序運行時的待爬隊列都不一樣了——這種情況下只能用分佈式,一個Master存儲隊列,其他多個Slave各自來取,這樣共享一個隊列,取的時候互斥也不會重複爬取。scrapy-redis是一款用得比較多的分佈式爬蟲框架。

 

數據質量管理

大量的頁面往往不會是結構完全一樣,而且大量的訪問也總會出現該訪問成功卻訪問不成功的情況,這些都是非常常見的狀況,因此單一的邏輯無法應對各種不可預知的問題,反映在結果上就是爬取的數據往往會有錯漏的情況。

  • try...except是Python中常用的異常診斷語句,在爬蟲中也可充分應用。一方面,同樣的字段可能在有的網頁上有,另外的網頁上就是沒有,這樣爬取該字段的語句就會出錯,然而這並不是自己邏輯或代碼的錯,用診斷語句就可以繞過這些網站的坑;另一方面,大規模爬蟲是一個耗時較長的過程,就像是千軍萬馬衝鋒,不能因爲中間掛了幾個而停止整體進程,所以採用這個語句可以跳過中間出現的各種自己產生或者網站產生的錯誤,保證爬蟲整體的持續進行。

  • 斷點續傳也是流程設計是重要的一塊。一個一旦啓動就必須要等它跑完,如果中途中斷就前功盡棄的爬蟲系統是非常不健壯的,因爲誰也無法預料中間會因各種原因中斷,而且估計也沒有誰會喜歡這種類似於被綁架的感覺。健壯的爬蟲系統應該是隨時都可以啓動,而且每次啓動都是爬剩下的而不是從頭開始重複爬,其實這個流程設計也比較簡單,如下圖所示:所有待爬的網頁total_urls分爲兩部分,一部分是已爬過的gotten_urls(初始化之前爲空),total_urls與gotten_urls的差集remained_urls就是剩餘要爬的網頁。total_urls是固定的,每執行一次爬蟲,gotten_urls就會增加,下一次啓動爬蟲程序計算的remained_urls就減少了,當remained_urls爲空表示完成全部爬蟲任務。這樣的斷點續傳流程設計可使爬蟲程序可以隨時停下,隨時啓動,並且每次啓動都不會做重複勞動。

大規模爬蟲流程總結

  • 錯漏校驗可以入庫之後進行,這一步就是把爬蟲過程中產生錯漏的記錄篩選出來清掉重新爬,這一步也很重要,保證數據質量才能繼續後續的流程。錯漏校驗就要結合業務自己來寫一套數據清洗流程。對於字段爲空的情況,有兩種產生原因:一是該網頁本來就沒有這個字段,這不是錯誤;另一種是由於網絡出錯沒有獲取到該字段,這是錯誤,要篩選出來清除——一般情況下可以通過status_code是否爲200來判斷網絡訪問是否出錯來判斷空字段是否是由於網絡出錯的原因造成的,對於特殊的status_code爲200仍不返回正常數據的就需特殊分析了。此外,可以通過某些字段固定的屬性來作爲篩選條件,比如名稱不能爲空(或者爲空就捨棄)、深圳地區的經度介於113和114之間等條件來過濾掉缺漏或者是網站反爬惡意傳回的錯誤數據。清洗邏輯越全面複雜,數據質量越高,後續使用數據時產生的問題就越少;這也是一塊需要深入思考的部分。

 

反反爬蟲

爬蟲的固定套路也就那麼多,各種網站爬取策略的不同就在於網站的反爬蟲機制不同,因此多作試驗,摸清網站的反爬機制,是大規模爬蟲的先行工作。爬蟲與反爬蟲是無休止的鬥爭,也是一個見招拆招的過程,但總體來說,以下方法可以繞過常見的反爬蟲。

  • 加上headers。這是最基礎的手段。加上了請求頭就可以僞裝成瀏覽器,混過反爬的第一道關卡;反之,連請求頭都不加,網站可以直接看出是程序在訪問而直接拒絕。一般的網站加上User-Agent就可以,反爬嚴格的網站則要加上cookie甚至各種參數都要加上。
  • 隨機延時。這是最簡單有效的一種手段。穩定性是大規模爬蟲的另一個核心問題,雖然與效率衝突。許多網站都會統計同一個IP一段時間內的訪問頻率,如果採集過快,會直接封禁IP。不要爲了一時爽而不加延時導致幾分鐘後IP就被封24小時,還不如老老實實地加延時慢慢爬一夜爬完。至於延時加多少因各個網站而異,但一般情況下延時個3~5秒就足夠了。
  • 如果頁面量實在太大,每次訪問設置的隨時延時也會成爲額外大量的時間成本。單個IP快速訪問會有被封的風險,這是就要用代理池,有兩點好處:一是降低某個IP單位時間內的訪問頻率,降低被封風險;二是即使IP被封,也有別的IP可以繼續訪問。代理池有免費和收費的,免費代理可以從許多網站上獲取(這也是一個爬蟲項目),但大部分都沒用,有用的小部分也會很快掛掉;收費代理好一點,但也好不了多少。高質量的代理成本就高了不少,這個要結合項目實際需求來考慮成本。所以,如果網站不封IP就可以不用代理,以免減慢訪問速度,增大被拒的概率。
  • 有的網站必須要登錄才能訪問,才能爬蟲。以知乎爲例,知乎的模擬登錄必較簡單,甚至現在都沒有對帳號和密碼加密,直接明文post就可以。請求頭的cookie含有登錄信息,而知乎的cookie壽命較長,所以可以直接在網站上人工登錄然後把cookie複製到代碼中;知乎目前的反爬機制是如果判斷是機器人就封帳號但不封IP——封IP是同樣的機器無法訪問,但卻可以用同樣的帳號在其他機器上訪問;封號是同樣的帳號在各種終端上都無法訪問,但同一臺機器上卻可以換號訪問。基於這種機制,爬知乎就不需要IP代理池而需要的是帳號池。舉另一個例子,騰訊有一個子網站,它也要求必須QQ登錄,而且cookie只有6分鐘的壽命,而且一個帳號一天只能訪問130次超過就封號,無論爬得再慢——這種情況下只能搞大量的QQ號進行自動登錄並不斷切換。
  • 如果有的網站的反爬機制實在太過喪心病狂,各種JS代碼邏輯十分複雜艱深,那隻能模擬瀏覽器了。模擬瀏覽器其實就是一種自動的瀏覽器訪問,與正常的用戶訪問很類似,所以可以跳過大部分的反爬機制,因爲你裝得實在太像正常用戶;不過缺點也很明顯,就是慢。所以可以用requests搞定的優先用requests,實在沒有辦法了再考慮模擬瀏覽器。
  • 驗證碼。驗證碼一出就蛋疼了……Python有自動識別圖像的包,不過對於大部分網站的驗證碼都無能爲力。寫一個自動識別驗證碼的程序理論上不是不行,但是這種複雜的機器學習項目一點都不比爬蟲系統本身難度低,從成本的角度考慮實在是得不償失——何況對於有些網站如谷歌,驗證碼識別是非常困難的。所以對於驗證碼問題,首先是躲過去儘量不要觸發驗證碼,實在觸發了只能乖乖人工去填驗證碼。

各種各樣的反爬機制也算是因垂斯聽,只有身經百戰,爬得多了,才能談笑風生,爬蟲水平不知道高到哪去了。有哪些有趣的反爬蟲手段?

 

爬蟲的道德節操和法律問題

  • 一些大型的網站都會有robot.txt,這算是與爬蟲者的一個協議。只要在robot.txt允許的範圍內爬蟲就不存在道德和法律風險,只不過實際上的爬蟲者一般都不看這個。
  • 控制採集速度。過快的採集會對網站服務器造成不小的壓力,如果是性能差的小站可能就會被這麼搞垮了。因此放慢採集速度相當於各退一步,既給網站減輕壓力,也降低自己被封禁的風險。
  • 爬蟲目前在法律上尚屬灰色地段,但爬別的網站用於自己的商業化用途也可能存在着法律風險。非法抓取使用“新浪微博”用戶信息 “脈脈”被判賠200萬元,這是國內的一條因爬蟲被判敗訴的新聞。所以各商業公司還是悠着點,特別是爬較爲隱私的數據。

 

發佈了12 篇原創文章 · 獲贊 26 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章