基礎(chǔ)知識
HTTP協(xié)議
我們?yōu)g覽網(wǎng)頁的瀏覽器和手機(jī)應(yīng)用客戶端與服務(wù)器通信幾乎都是基于HTTP協(xié)議,而爬蟲可以看作是一個另類的客戶端,它把自己偽裝成瀏覽器或者手機(jī)應(yīng)用客戶端辙培,按照自己的邏輯貪婪的向服務(wù)器索取數(shù)據(jù)线定,如何向服務(wù)器索取數(shù)據(jù)早芭,所以了解HTTP協(xié)議就顯得很有必要了绩鸣。
HTTP協(xié)議中文名稱是超文本傳輸協(xié)議怀大,是一個基于請求與響應(yīng)模式的、無狀態(tài)的呀闻、應(yīng)用層的協(xié)議,城鄙鳎基于TCP的連接方式捡多。請求和響應(yīng)模式很好理解蓖康,客戶端發(fā)送請求,服務(wù)器響應(yīng)客戶端的請求垒手,就像學(xué)校食堂打菜一樣蒜焊,你和打菜阿姨說要哪份菜,她才會給你盛哪份菜科贬。
無狀態(tài)是指協(xié)議對于事務(wù)處理沒有記憶能力泳梆。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息,則它必須重傳榜掌,這樣可能導(dǎo)致每次連接傳送的數(shù)據(jù)量增大优妙。另一方面,在服務(wù)器不需要先前信息時它的應(yīng)答就較快憎账。形象點(diǎn)說套硼,可以把服務(wù)器看成是沒有記憶的大學(xué)食堂打飯打菜,在每次請求中胞皱,阿姨并不知道你之前有沒有打過菜邪意,也不知道你是不是合法的學(xué)生,所以你只能一邊舉著學(xué)生證一邊和阿姨說我要這個菜反砌,阿姨看到你的學(xué)生證后才會給你打菜雾鬼,而這個學(xué)生證就是你每次需要重傳的數(shù)據(jù)信息。
當(dāng)我們在瀏覽器地址欄中輸入http://www.bilibili.com 并敲入回車后宴树,瀏覽器會構(gòu)造HTTP請求發(fā)送到服務(wù)器策菜,在收到服務(wù)器的HTTP響應(yīng)后,瀏覽器會解析頁面森渐,繼續(xù)向服務(wù)器請求圖片做入、視頻、js腳本等數(shù)據(jù)同衣,直到頁面加載完成竟块,最終展示給我們的就是B站主頁了。這是我用Fiddler抓的包耐齐,展示的是HTTP最原生的面貌浪秘,接下來我就根據(jù)這張圖具體的講解HTTP協(xié)議,以及寫爬蟲需要關(guān)注的一些點(diǎn)埠况。
HTTP請求由三部分組成耸携,分別是: 請求行、消息報頭辕翰、請求正文夺衍。 在接收和解釋請求消息后,服務(wù)器返回一個HTTP響應(yīng)消息喜命,HTTP響應(yīng)也是由三個部分組成沟沙,分別是:狀態(tài)行河劝、消息報頭、響應(yīng)正文矛紫。
HTTP方法
HTTP請求的請求行以一個方法符號開頭赎瞎,以空格分開,后面跟著請求的URI和協(xié)議的版本颊咬。請求方法都是大寫务甥,有很多種,常見的有GET POST DELETE PUT喳篇,各種方法之間的區(qū)別不大敞临。
這里羅列了一些常用的方法,一般來講杭隙,GET表示向服務(wù)器請求URI對應(yīng)的資源哟绊,POST表示向服務(wù)器提交數(shù)據(jù),DELETE表示刪除數(shù)據(jù)痰憎,PUT表示修改數(shù)據(jù)票髓。但這都是一種約定,沒有強(qiáng)制的要求铣耘,如果你碰見用DELETE方法提交數(shù)據(jù)也沒必要大驚小怪洽沟。在實(shí)際寫爬蟲的過程中,我們只需要按照抓包請求構(gòu)造數(shù)據(jù)即可蜗细,沒有必要在意用了什么方法裆操。
報頭字段
重點(diǎn)講解幾個寫爬蟲需要關(guān)注的字段
- User-Agent 出現(xiàn)在請求報頭中,表示客戶端的操作系統(tǒng)炉媒、瀏覽器型號版本等信息踪区。服務(wù)器可以根據(jù)此報頭向客戶端返回不同的頁面以適應(yīng)客戶端。有些網(wǎng)站(知乎)會校驗此報頭吊骤,不填寫或者不主流的報頭都不能拿到正常的頁面缎岗。因此自己在寫爬蟲的時候最好將從瀏覽器中拷貝到代碼中。
- Cookie 出現(xiàn)在請求抱頭中白粉,前面我們說過HTTP是基于請求與響應(yīng)模式并且無狀態(tài)的協(xié)議传泊,之前舉了打菜阿姨的例子,Cookie就相當(dāng)于每次請求中的學(xué)生證鸭巴,它可以記錄用戶的身份信息眷细。當(dāng)我們自己寫爬蟲的時候,如果需要登陸鹃祖,并且登陸又有驗證碼或者短信驗證時溪椎,最簡單的方法就是從瀏覽器中把cookie拷貝到爬蟲中,就可以騙過服務(wù)器了。
- Set-Cookie 出現(xiàn)在響應(yīng)抱頭中池磁,讓客戶端更新頁面關(guān)聯(lián)的Cookie奔害,還是拿食堂阿姨的例子楷兽,如果你的響應(yīng)報頭有這個字段地熄,意思就是阿姨重新給你了一個學(xué)生證,下次打飯你得用最新的學(xué)生證芯杀,原來的學(xué)生證不好使端考。如果你在模擬瀏覽器或者客戶端登陸,需要將此報頭更新已有的Cookie揭厚,不過Scrapy和requests都可以自動更新却特,因此不需要你再手動設(shè)置。
- Content-Type 標(biāo)明請求正文或者響應(yīng)正文的格式筛圆,客戶端或者服務(wù)器會根據(jù)此字段選擇合適的方式解析正文內(nèi)容裂明,以下是一些常見的值
- Content-Length 標(biāo)明請求正文或者響應(yīng)正文的長度,在使用requests構(gòu)造請求的時候太援,我們不需要顯式的加上此字段闽晦,requests會根據(jù)請求正文自動計算添加。
- Content-Encoding 在某些情況下提岔,正文會講過壓縮后傳輸仙蛉,此字段會指明壓縮的類型(gzip和壓縮參數(shù))
- Transfer-Encoding 如果正文內(nèi)容過長,HTTP協(xié)議允許將此字段設(shè)置為chunked碱蒙,然后分塊傳輸響應(yīng)正文
- Connection 在HTTP1.1之前的版本荠瘪,不支持持久連接,所謂的持久鏈接意思就是:HTTP協(xié)議一般通過TCP協(xié)議實(shí)現(xiàn)赛惩,客戶端和服務(wù)器經(jīng)過TCP三次握手建立連接哀墓,在請求和響應(yīng)結(jié)束之后,此連接繼續(xù)保持喷兼,當(dāng)之后還有請求的時候篮绰,就不需要重新通過三次握手再建立連接,這樣可以極大的降低客戶端和服務(wù)器的IO負(fù)載褒搔。
在自己寫爬蟲的時候阶牍,我們可以根據(jù)瀏覽器的抓包數(shù)據(jù)有選擇的添加一些請求報頭,其實(shí)大部分情況下都可以直接使用瀏覽器中的請求頭星瘾,為了避免不必要的麻煩走孽,盡可能像的模仿瀏覽器總是沒有錯的。
響應(yīng)碼
響應(yīng)消息的第一行的狀態(tài)行包括HTTP的協(xié)議版本琳状、狀態(tài)碼磕瓷、狀態(tài)碼含義。按照約定
- 2xx表示請求成功
- 3xx表示重定向
- 4xx表示客戶端錯誤(403 Forbiden 404 Not Found)
- 5xx表示服務(wù)器錯誤(502 網(wǎng)關(guān)錯誤)
更多HTTP參考:
- http://www.ruanyifeng.com/blog/2016/08/http.html (阮一峰)
- https://book.douban.com/subject/10746113/ (HTTP權(quán)威指南)
爬蟲開發(fā)
一般來說開發(fā)爬蟲的過程是這樣的
- 抓包分析獲取數(shù)據(jù)的URL
- 通過python從上一步的URL獲取數(shù)據(jù)
- 從上一步獲取的HTML頁面或者JSON數(shù)據(jù)中解析出感興趣的數(shù)據(jù)
- 存儲數(shù)據(jù)
下面就講解這四個關(guān)鍵點(diǎn)
抓包發(fā)包工具
寫爬蟲的第一步就是分析想要的數(shù)據(jù)瀏覽器是通過什么URL拿到的,抓包也就在所難免困食。最好用的抓包工具當(dāng)然是谷歌瀏覽器了边翁,右鍵檢查,選中網(wǎng)絡(luò)硕盹,重新刷新頁面就可以看到加載此網(wǎng)頁所有的HTTP請求了符匾,如果此鏈接有跳轉(zhuǎn)地址,跳轉(zhuǎn)之前的HTTP請求會被清掉瘩例,所以記得選上preserve log啊胶,尤其是登陸的時候,一般都會有跳轉(zhuǎn)垛贤。
再介紹另外兩個HTTP抓包工具——Fiddler和Charles焰坪,分別在windows和macos使用。它們可以為我們展示更多HTTP的細(xì)節(jié)聘惦,將請求和響應(yīng)都調(diào)至Raw模式下某饰,我們就可以一睹HTTP請求和響應(yīng)的真實(shí)面貌。
通過抓包分析出具體的URL后善绎,想進(jìn)一步確認(rèn)自己構(gòu)造的參數(shù)和報頭能否正確獲取到數(shù)據(jù)黔漂,應(yīng)該怎么做呢?不怕涂邀,postman可以幫你瘟仿,你可以很輕松的選擇方法,定義header比勉,添加各種類型的body劳较。
python請求數(shù)據(jù)
講完了基本的HTTP協(xié)議知識后,大家可能會疑問那我該如何模仿瀏覽器或者手機(jī)客戶端去向服務(wù)器發(fā)送HTTP請求呢浩聋?python的原生庫urllib观蜗、第三方庫requests、pycurl等都支持HTTP協(xié)議衣洁,既然有這么多工具可以用墓捻,大家可能就又有疑問該選擇哪個工具了。在此我特地安利大家用一下requests坊夫,它讓爬蟲變得如此簡單砖第,讓你再也不用為字符編碼、重定向环凿、cookie梧兼、響應(yīng)解壓縮煩惱了。如果你堅持用原生的庫智听,那么有以下問題需要你一一解決羽杰,這些都是當(dāng)年自己趟過的坑渡紫,絕非危言聳聽。
- 需要自己判斷服務(wù)器返回數(shù)據(jù)的編碼格式考赛,如果這個地方你不能正確判斷惕澎,那恭喜你之后的每一步,你都必須面對亂碼的問題
- 重定向颜骤,urllib不能自動判斷重定向唧喉,需要自己解析重定向的鏈接并重新請求
- 如果模擬登陸,你必須要手動保證Cookie正確更新和發(fā)送
- 很多情況下響應(yīng)正文是壓縮過的复哆,需要做解壓處理
- 對于比較長的響應(yīng)正文欣喧,服務(wù)器會將正文分段傳輸,所以還需要你做拼接操作
- 原生的urllib對HTTPS和持久連接都支持不好
當(dāng)你花了一整天梯找,寫了好幾百行的代碼終于解決上面的問題后,而你旁邊的同事可能早已經(jīng)把數(shù)據(jù)下載完并愉快的約妹子去了益涧。所以用requests吧锈锤,兄弟們用了都說好。下面我用兩個例子講解一下如何用requests獲取想要的數(shù)據(jù)闲询,并教你如何解決這些問題:
- 如何發(fā)送不同方法的請求
- 如何保存cookie
- 如何添加代理
- 如何處理編碼問題
B站
假如我想下載B站里面某位小姐姐所有上傳的視頻久免,應(yīng)該怎么辦呢?首先你需要找到這位小姐姐的視頻主頁
但是通過谷歌瀏覽器右鍵查看頁面源碼扭弧,沒有從html中找到這些視頻的播放信息阎姥,唯一的可能就是視頻數(shù)據(jù)是通過js腳本調(diào)用服務(wù)器獲取,然后生成的這張頁面鸽捻。爬蟲小白可能會疑問呼巴,難道我需要像瀏覽器一樣分析js腳本,然后模擬js執(zhí)行嗎御蒲?其實(shí)不用這么復(fù)雜衣赶,只需要簡單的分析抓包結(jié)構(gòu),就可以找到請求URL了厚满。
那么問題又來了府瞄,這個URL的其他參數(shù)是干啥的呢?憑經(jīng)驗碘箍,mid肯定是這位小姐姐的用戶id遵馆,page和pagesize是負(fù)責(zé)分頁用的,keyword和是用來搜索的關(guān)鍵字丰榴,order是排序選項货邓,剩下的tid是干啥的呢?其實(shí)寫爬蟲很多時候都會遇到這種問題多艇,不知道某個參數(shù)的含義逻恐,也不確定正確的取值范圍,需要一些嘗試和運(yùn)氣,這里我們不管它就好复隆。而返回的字段中有一個aid拨匆,那肯定是視頻的id,有這個就可以自己拼接出播放鏈接了挽拂。
是不是很簡單惭每,通過response.ok查看請求是否正確返回,因為此接口的數(shù)據(jù)為json格式亏栈,直接通過response.json()就可以直接拿到格式化的數(shù)據(jù)台腥。
知乎
雖然現(xiàn)在知乎對未登錄用戶展示的內(nèi)容越來越多,但是仍會有一些限制,用爬蟲模擬登陸可以之后再去爬取數(shù)據(jù)蕊苗,可以避免很多不必要的麻煩痹仙,現(xiàn)在就講一講如何用requests模擬用戶登陸。
還是和之前一樣峻汉,在登陸頁面打開谷歌瀏覽器的抓包窗口,輸入用戶名和密碼點(diǎn)擊確定脐往,然后在茫茫請求中找到發(fā)送登陸信息的那個HTTP請求即可休吠,功夫不負(fù)有心人,我們終于找到了登陸的請求业簿。
等等瘤礁,請求里面還有一個_xsrf,這是一個什么鬼參數(shù)梅尤,其實(shí)呢這是一個防止跨站請求偽造而生成的一個隨機(jī)數(shù)柜思,可以通過解析https://www.zhihu.com/#signin 頁面獲取,這一部分我在下面會講解如何HTML獲取數(shù)據(jù)克饶,現(xiàn)在假設(shè)我們已經(jīng)拿到這個數(shù)據(jù)了酝蜒,如何將用戶名和密碼登陸呢?
如果我們想要自動保存Cookie信息矾湃,只需要生成一個Session對象亡脑,之后所有的請求通過此對象完成,requests會像瀏覽器一樣自動更新cookie信息邀跃,并在每次請求的時候加上cookie霉咨,因此在成功的發(fā)送post登陸請求之后,就可以用session在保持登陸狀態(tài)請求數(shù)據(jù)了拍屑。需要注意的是在請求的時候我特意去掉了Cookie和Content-Length報頭途戒,因為requests會自動加上,所以不需要我們特意關(guān)注僵驰。
更多關(guān)于requests的使用可以查看官方文檔:
英文: http://docs.python-requests.org/en/master/
中文: http://cn.python-requests.org/zh_CN/latest/user/quickstart.html
python解析數(shù)據(jù)
因為個人在解析數(shù)據(jù)的時候遇到過很多編碼的坑喷斋,所以在繼續(xù)講解之前告訴大家一些如何避免編碼問題的方法唁毒。python2中有兩種字符串:unicode和str,它們分別對應(yīng)python3中的str和bytes星爪。如何定義這兩種類型的變量在下圖中給大家列出來了浆西。
以python3為例講解這兩種類型的區(qū)別。python3中的str每一個字符可以存儲一個英文字母顽腾、一個漢字甚至一個emoji表情近零,它可以通過特定的編碼方式,例如utf-8或者gbk生成bytes抄肖,在不同的編碼格式下久信,可能需要2-3個字符常能表示一個漢字。bytes可以指定解碼格式解碼生成str漓摩,如果指定的解碼格式不匹配裙士,就會導(dǎo)致亂碼問題。為了避免亂碼問題幌甘,最好的方式就是使用str潮售,等到需要寫入文件或者數(shù)據(jù)庫的時候,再指定寫入的編碼格式锅风,用好這個準(zhǔn)則,我們可以避免百分之九十的編碼問題鞍泉。
HTTP響應(yīng)的數(shù)據(jù)格式有很多皱埠,例如文本、json咖驮、html边器,對應(yīng)的解析方式也很多。通用一點(diǎn)托修,用python內(nèi)置庫正則匹配找到想要的數(shù)據(jù)忘巧,但是這種方法相對來說比較麻煩,而且不好維護(hù)睦刃,比較適合文本類型的數(shù)據(jù)砚嘴,但HTTP響應(yīng)正文基本都是json和HTML,這種方式適用面比較窄涩拙。
當(dāng)請求的數(shù)據(jù)是json格式時际长,我們可以很方便的用requests反序列化返回內(nèi)容,取出感興趣的數(shù)據(jù)兴泥。但是當(dāng)HTTP返回的數(shù)據(jù)是html的時候工育,我們該如何操作,就像剛才知乎登陸的例子中搓彻,如何快速從html中解析想要的數(shù)據(jù)呢如绸?
專門用來解析html的第三方庫有很多嘱朽,例如beautifulsoup、pyquery怔接。個人推薦使用pyquery搪泳,因為它可以使用jquery的方式選取元素,而且支持xpath蜕提,以后上手scrapy會很容易森书。繼續(xù)上面登陸知乎的例子,登陸時需要的_xsrf實(shí)際上在 https://www.zhihu.com/#signin 頁面里面谎势,只要先請求到這個頁面凛膏,然后解析出_xsrf,配合之前的登陸請求脏榆,我們就可以完整的實(shí)現(xiàn)用python模擬瀏覽器登陸知乎了猖毫。
使用起來是不是相當(dāng)?shù)暮唵危覀冎灰ㄟ^谷歌瀏覽器找到對應(yīng)DOM元素须喂,根據(jù)屬性名就可以非秤醵希快速的找到想要的數(shù)據(jù)。需要注意的是response.content和response.text坞生,這都是返回的body正文仔役,但是前者是bytes,后者是str是己,requests已經(jīng)幫助我們把響應(yīng)正文按照正確的編碼格式進(jìn)行了解碼又兵,根據(jù)我們之前的闡述的原則,盡量使用str卒废,所以26這個地方我用的是response.text沛厨。
更多關(guān)于pyquery的使用可以參考官方文檔: https://pythonhosted.org/pyquery/
存儲數(shù)據(jù)
根據(jù)數(shù)據(jù)量的不同以及數(shù)據(jù)類型的不同,數(shù)據(jù)的存儲選擇也很多摔认。如果爬取的是圖片逆皮、音頻、視頻等多媒體文件参袱,直接按照文件形式存儲就好了电谣。如果是一些文本,數(shù)字等數(shù)據(jù)蓖柔,一般有這么幾種選擇:
- 輸出到屏幕
- 寫入文件(txt csv)
- 寫入數(shù)據(jù)庫 (mysql sqlite mongo)
如果數(shù)據(jù)量非常小辰企,可以選擇直接輸出到屏幕(這種情況貌似也不需要爬蟲),因為終端存儲的數(shù)據(jù)量很少况鸣,而且因為沒有持久化牢贸,關(guān)閉窗口就意味著數(shù)據(jù)丟失,不建議使用镐捧。
在數(shù)據(jù)量小且不愿意折騰數(shù)據(jù)庫的情況下潜索,可以把爬取的數(shù)據(jù)寫入文件臭增,但是這種情況不能隨取隨用,也不方便做數(shù)據(jù)分析竹习,需要手動處理誊抛。
當(dāng)數(shù)據(jù)量較多,而且需要快捷的分析數(shù)據(jù)整陌,推薦使用數(shù)據(jù)庫存儲數(shù)據(jù)拗窃,大型的數(shù)據(jù)庫mysql, mongo功能齊全,可以分方便的進(jìn)行數(shù)據(jù)分析泌辫,而且也很容易實(shí)現(xiàn)分布式擴(kuò)展随夸,當(dāng)你需要多進(jìn)程甚至多機(jī)器運(yùn)行爬蟲的時候,這些數(shù)據(jù)庫可能是最好的選擇震放。sqlite相對來說功能要少很多宾毒,python原生支持,依賴少殿遂,數(shù)據(jù)量不算太大的情況下可以考慮使用诈铛。
因為這部分內(nèi)容太多太深,感興趣的童鞋如果想深入了解一些墨礁,這里列出一些文檔供大家參考:
python操作sqlite: http://www.runoob.com/sqlite/sqlite-python.html
python操作mysql: http://www.runoob.com/python/python-mysql.html
python操作mongo: https://www.oschina.net/question/54100_27233
爬蟲示例
下面給出一個簡單的例子幢竹,為大家展示如何使用上述python庫實(shí)現(xiàn)一個完整的爬蟲。一些熱門的知乎話題最多有1000條精華回答恩静,這個例子就是爬取這些精品答案妨退。圖示頁面就是回答列表,每頁有二十個答案蜕企,最多有五十頁。但是此頁面沒有完整的回答信息冠句,需要通過顯示全部對應(yīng)的鏈接進(jìn)入回答詳情頁才能獲取完整的答案轻掩,所以我們的爬蟲策略就是通過回答列表找到所有精華回答鏈接,再通過回答鏈接獲取內(nèi)容懦底。而且這些頁面不需要登陸也能訪問唇牧,因此可以省去模擬登陸。
開發(fā)環(huán)境
python是跨平臺語言聚唐,但不同平臺不同版本的python略微有一些差異丐重,考慮到大家使用windows平臺的較多,我在windows和ubuntu的python3.5驗證過此代碼杆查,其他平臺和其他python版本下可能需要做一些修改扮惦。集成開發(fā)環(huán)境推薦使用Pycharm,這是一個跨平臺良心IDE亲桦,各大操作系統(tǒng)下都有免費(fèi)的社區(qū)版本可以下載崖蜜。
運(yùn)行代碼
代碼鏈接
https://github.com/LiuRoy/sakura/blob/master/spider/crawl.py
https://github.com/LiuRoy/sakura/blob/master/spider/tables.sql
安裝依賴庫:
pip install requests pyquery SQLAlchemy
運(yùn)行代碼:
python scrawl.py
代碼解釋
通過谷歌瀏覽器抓包分析浊仆,可以通過 https://www.zhihu.com/topic/19553155/top-answers?page=2 頁面獲取每個話題不同分頁下的回答鏈接 https://www.zhihu.com/question/27189372/answer/38712987 ,在此頁面中就可以獲取問題豫领、回答抡柿、點(diǎn)贊數(shù)、標(biāo)簽等信息等恐。
因為數(shù)據(jù)量不大洲劣,采用sqlite存儲,可以很方便的用命令行或者桌面客戶端查看數(shù)據(jù)课蔬。
反爬蟲策略和應(yīng)對方式
稍微大一些的網(wǎng)站都會有一些反爬蟲策略囱稽,簡單一點(diǎn)的根據(jù)User-Agent過濾,例如知乎购笆,我們只需要設(shè)置為和瀏覽器相同即可粗悯。復(fù)雜一點(diǎn)的會限制非登陸用戶,也只需要按照之前例子中的方式登陸同欠,或者干脆先用瀏覽器登陸好样傍,然后在第一次訪問的時候帶上瀏覽器中的cookie就好,實(shí)現(xiàn)起來難度不大铺遂。但是有不少網(wǎng)站衫哥,例如豆瓣和github,在檢測到某一客戶端頻繁訪問后襟锐,會直接封鎖ip撤逢,這個問題解決起來就相當(dāng)?shù)募趾偷疤哿恕?/p>
解決方法也挺簡單,我們只需要找到足夠多的代理ip就可以了粮坞,只要策略得當(dāng)蚊荣,短時間內(nèi)不要過度頻繁的使用同一ip代碼,或者當(dāng)某一ip地址被封鎖后馬上切換到其他的ip代理莫杈,這樣就可以保證高效的爬取數(shù)據(jù)了互例。那如何找到代理ip并且如何使用了,其實(shí)免費(fèi)的代理ip很多筝闹,我們用百度搜索代理ip就可以找到很多網(wǎng)站媳叨,例如:http://www.ip181.com/。
找到代理ip后关顷,就可以用上面的方式很輕松的使用代理ip了糊秆,但是網(wǎng)上免費(fèi)的代理ip質(zhì)量不好,很多不可用议双,而且速度慢痘番、不穩(wěn)定,請求的時候最好設(shè)置一下超時時間聋伦。我之前在爬github的時候夫偶,會專門寫一個爬蟲從這些網(wǎng)站搜集代理ip地址界睁,一旦檢測到被github封鎖,就隨機(jī)選取一個代理ip使用兵拢,如果發(fā)現(xiàn)代理ip不可用翻斟,不斷的更換知道可用的代理ip為止,每個代理ip使用的次數(shù)也會有一定的限制说铃,保證爬蟲在整個執(zhí)行期間不會因為ip封鎖而不可用访惜。
異常及性能
曾經(jīng)我遇到過這樣的狀態(tài),寫好并運(yùn)行爬蟲一個小時之后腻扇,因為網(wǎng)絡(luò)抖動或者某一種特殊的頁面導(dǎo)致解析失敗债热,整個爬蟲運(yùn)行終止,這就蛋疼了幼苛。如果日志打印不充分窒篱,我連運(yùn)行失敗的原因都清楚,更別說修復(fù)問題舶沿,即使修復(fù)好重新運(yùn)行墙杯,又要花一個小時才能到剛才的進(jìn)度,費(fèi)時費(fèi)力括荡。
爬蟲出現(xiàn)異常的情況實(shí)在是太多:網(wǎng)絡(luò)抖動高镐、解析頁面失敗、反爬蟲被屏蔽畸冲、寫入數(shù)據(jù)異常嫉髓、代碼邏輯錯誤等等都會導(dǎo)致進(jìn)程終止,而一般爬蟲需要數(shù)小時甚至數(shù)天數(shù)周的運(yùn)行邑闲,每一次的運(yùn)行失敗都是時間巨大的浪費(fèi)算行。因此一定需要在可能出現(xiàn)異常的地方接住異常,保證爬蟲不會終止苫耸,并記錄日志纱意,這些錯誤日志不僅可以快速的幫助我們定位錯誤,在出錯不多的情況下鲸阔,我們甚至不需要修改代碼重新運(yùn)行,只需要人肉補(bǔ)全這些數(shù)據(jù)就好迄委。
如果發(fā)現(xiàn)自己的爬蟲運(yùn)行效率太低褐筛,爬取速度太慢,并發(fā)對于提升爬蟲速度是一個不錯解決方案叙身,因為GIL的存在渔扎,多進(jìn)程并發(fā)模式對于python提速更優(yōu),我們可以使用生產(chǎn)者消費(fèi)者的模式將爬蟲任務(wù)進(jìn)行拆分信轿,從而實(shí)現(xiàn)多進(jìn)程或者分布式晃痴。一般來說可以將HTTP請求的數(shù)據(jù)残吩、解析數(shù)據(jù)、存儲數(shù)據(jù)分別用不同的進(jìn)程實(shí)現(xiàn)倘核,這些進(jìn)程之間通過消息隊列進(jìn)行通信泣侮,保證每個進(jìn)程無狀態(tài),就可以非常容易的實(shí)現(xiàn)多進(jìn)程擴(kuò)展紧唱。即使某一類進(jìn)程出現(xiàn)異常活尊,也不需要重新啟動所有的進(jìn)程,只需要修復(fù)好代碼重新啟動漏益,就可以實(shí)現(xiàn)斷點(diǎn)續(xù)爬了蛹锰。
一些常用的分布式工具:redis、rabbitmq绰疤、scrapy铜犬、celery、you-get
redis: http://www.redis.cn/
rabbitmq: http://www.rabbitmq.com/documentation.html
scrapy: http://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/tutorial.html
celery: http://docs.jinkan.org/docs/celery/
you-get: https://github.com/soimort/you-get/wiki/中文說明