大規模爬蟲流程總結

Hellowongwong發表於2017-04-20

爬蟲是一個比較容易上手的技術,也許花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萬元,這是國內的一條因爬蟲被判敗訴的新聞。所以各商業公司還是悠著點,特別是爬較為隱私的資料。
原文:http://www.toutiao.com/i6411029854317183489/

相關文章