天天看點

大規模爬蟲流程總結

爬蟲是一個比較容易上手的技術,也許花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比較簡單,支援xpath和cssselector兩種途徑,而且像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萬元,這是國内的一條因爬蟲被判敗訴的新聞。是以各商業公司還是悠着點,特别是爬較為隐私的資料。

本文作者:佚名

來源:51cto