原文 http://www.xgezhang.com/python_scrapy_redis_crawler.html
爬蟲技術,無論是在學術領域,還是在工程領域,都扮演者非常重要的角色。相比於其他技術,爬蟲技術雖然在實現上比較簡單,沒有那么多深奧的技術難點,但想要構建一套穩定、高效、自動化的爬蟲框架,也並不是一件容易的事情。這里筆者打算就個人經驗,介紹一種分布式爬蟲框架的實現方法和工作原理,來給剛剛入門爬蟲的同學們一點啟發和提示。同時也希望大牛們能發表一些看法。
注:本文是對框架的宏觀介紹,沒有涉及具體代碼,適合接觸過爬蟲和scrapy的讀者。如果您對爬蟲完全不了解,或者是剛剛入門,可以先看一些簡單的爬蟲教程和示例,這里貼幾個鏈接:
人人網爬蟲(針對老版本,目前可能沒法用,但里面的技術是通用的):http://www.xgezhang.com/python_renren_spider_1.html
京東登陸爬蟲: http://www.xgezhang.com/python_crawler_jingdong.html
scrapy中文教程: http://scrapy-chs.readthedocs.org/zh_CN/latest/intro/tutorial.html
爬蟲,網頁Xpath輔助分析工具: http://www.xgezhang.com/xpath_helper.html
框架的幾個關鍵技術點:
scrapy : 實現爬蟲的主體。scrapy是目前非常熱門的一種爬蟲框架,它把整個爬蟲過程分為了多個獨立的模塊,並提供了多個基類可以供我們去自由擴展,讓爬蟲編寫變得簡單而有邏輯性。並且scrapy自帶的多線程、異常處理、以及強大的自定義Settings也讓整個數據抓取過程變得高效而穩定。
scrapy-redis:一個三方的基於redis的分布式爬蟲框架,配合scrapy使用,讓爬蟲具有了分布式爬取的功能。g ithub地址: https://github.com/darkrho/scrapy-redis ,
mongodb 、mysql 或其他數據庫:針對不同類型數據可以根據具體需求來選擇不同的數據庫存儲。結構化數據可以使用mysql節省空間,非結構化、文本等數據可以采用mongodb等非關系型數據提高訪問速度。具體選擇可以自行百度谷歌,有很多關於sql和nosql的對比文章。
其他:一台高性能的服務器。
分布式原理:
scrapy-redis實現分布式,其實從原理上來說很簡單,這里為描述方便,我們把自己的核心服務器稱為master,而把用於跑爬蟲程序的機器稱為slave。
我們知 道,采用scrapy框架抓取網頁,我們需要首先給定它一些start_urls,爬蟲首先訪問start_urls里面的url,再根據我們的具體邏輯,對里面的元素、或者是其他的二級、三級頁面進行抓取。而要實現分布式,我們只需要在這個starts_urls里面做文章就行了。
我們在master上搭建一個redis數據庫(注意這個數據庫只用作url的存儲,不關心爬取的具體數據,不要和后面的mongodb或者mysql混淆),並對每一個需要爬取的網站類型,都開辟一個單獨的列表字段。通過設置slave上scrapy-redis獲取url的地址為master地址。這樣的結果就是,盡管有多個slave,然而大家獲取url的地方只有一個,那就是服務器master上的redis數據庫。
並且,由於scrapy-redis自身的隊列機制,slave獲取的鏈接不會相互沖突。這樣各個slave在完成抓取任務之后,再把獲取的結果匯總到服務器上(這時的數據存儲不再在是redis,而是mongodb或者 mysql等存放具體內容的數據庫了)
這種方法的還有好處就是程序移植性強,只要處理好路徑問題,把slave上的程序移植到另一台機器上運行,基本上就是復制粘貼的事情。
url的生成:
看到這里,有同學應該已經想到了,上文只介紹了slave如何去讀取url,那么這些url是怎么出現的呢?
首先明確一點,url是在master而不是slave上生成的。
對於每一個門類的urls(每一個門類對應redis下的一個字段,表示一個url的列表),我們可以單獨寫一個生成url的腳本。這個腳本要做的事很簡單,就是按照我們需要的格式,構造除url並添加到redis里面。
對於slave,我們知道,scrapy可以通過Settings來讓爬取結束之后不自動關閉,而是不斷的去詢問隊列里有沒有新的url,如果有新的url,那么繼續獲取url並進行爬取。利用這一特性,我們就可以采用控制url的生成的方法,來控制slave爬蟲程序的爬取。
定時抓取:
有了上面的介紹,定時抓取的實現就變得簡單了,我們只需要定時的去執行url生成的腳本即可。這里推薦linux下的crontab指令,能夠非常方便的制定定時任務,具體的介紹大家可以自行查看文檔。
程序化管理、web管理:
上述方法雖然能夠實現一套完整的流程,但在具體操作過程中還是比較麻煩,可能的話還可以架構web服務器,通過web端來實現url的添加、爬蟲狀態的監控等,能夠減輕非常大的工作量。這些內容如果要展開實在太多,這里就只提一下。
總結:
其實對於已有的scrapy程序,對其擴展成分布式程序還是比較容易的。總的來說就是以下幾步:
- 找一台高性能服務器,用於redis隊列的維護以及數據的存儲。
- 擴展scrapy程序,讓其通過服務器的redis來獲取start_urls,並改寫pipeline里數據存儲部分,把存儲地址改為服務器地址。
- 在服務器上寫一些生成url的腳本,並定期執行。
介紹完整個流程,再來介紹一些常見的防抓取屏蔽的方法:
- 設置download_delay,這個方法基本上屬於萬能的,理論上只要你的delay足夠長,網站服務器都沒辦法判斷你是正常瀏覽還是爬蟲。但它帶來的副作用也是顯然的:大量降低爬取效率。因此這個我們可能需要進行多次測試來得到一個合適的值。有時候download_delay可以設為一個范圍隨機值。
- 隨機生成User-agent:更改User-agent能夠防止一些403或者400的錯誤,基本上屬於每個爬蟲都會寫的。這里我們可以重寫scrapy 里的middleware,讓程序每次請求都隨機獲取一個User-agent,增大隱蔽性。具體實現可以參考http://www.sharejs.com/codes/python/8310
- 設置代理IP池:網上有很多免費或收費的代理池,可以借由他們作為中介來爬。一個問題是速度不能保證,第二個問題是,這些代理很多可能本來就沒辦法用。因此如果要用這個方法,比較靠譜的做法是先用程序篩選一些好用的代理,再在這些代理里面去隨機、或者順序訪問。
- 設置好header里面的domian和host,有些網站,比如雪球網會根據這兩項來判斷請求來源,因此也是要注意的地方。
暫時想到這么多,以后有新的發現再補充,也歡迎各位來討論。
轉載請注明出處。