之前寫的爬蟲,無論是單線程,多線程異步等都是在自己的電腦上運行。
好處是單個爬蟲方便管理,調試;但當有了大量的URL需要爬取,用分布式爬蟲無疑是最好的選擇。
我的測試代碼以實習僧網為目標網站,約2w個URL,單個scrapy與3個scrapy-redis分布式時間比約為 5: 1
這篇文章會通過一個例子詳細介紹scrapy-redis原理及其實現過程。
0.安裝scrapy_redis
windows、ubuntu安裝請參看:http://blog.fens.me/linux-redis-install/
centos7安裝請參看:https://www.cnblogs.com/zjl6/p/6742673.html
注意:建議設置redis密碼進行遠程連接,或者添加安全組規則ip白名單,直接暴露端口容易被黑。
1.首先介紹一下:scrapy-redis框架
scrapy-redis:一個三方的基于redis的分布式爬蟲框架,配合scrapy使用,讓爬蟲具有了分布式爬取的功能。
github地址: https://github.com/darkrho/scrapy-redis
一篇知乎介紹《scrapy-redis 和 scrapy 有什么區別?》
2.再介紹一下:分布式原理
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上的程序移植到另一臺機器上運行,基本上就是復制粘貼的事情。
3.分布式爬蟲的實現:
1.使用兩臺機器,一臺是win10,一臺是ubuntu16.04,分別在兩臺機器上部署scrapy來進行分布式抓取一個網站
2.ubuntu16.04的ip地址為39.106.155.194,用來作為redis的master端,win10的機器作為slave
3.master的爬蟲運行時會把提取到的url封裝成request放到redis中的數據庫:“dmoz:requests”,并且從該數據庫中提取request后下載網頁,再把網頁的內容存放到redis的另一個數據庫中“dmoz:items”
4.slave從master的redis中取出待抓取的request,下載完網頁之后就把網頁的內容發送回master的redis
5.重復上面的3和4,直到master的redis中的“dmoz:requests”數據庫為空,再把master的redis中的“dmoz:items”數據庫寫入到mongodb中
6.master里的reids還有一個數據“dmoz:dupefilter”是用來存儲抓取過的url的指紋(使用哈希函數將url運算后的結果),是防止重復抓取的
(注:master與salve已經安裝了MongoDB,Redis,scrapy,MySQL。)
4.完整實現過程
1、完成編碼,多復制幾份,把其中一份放到ubuntu作為master,其他幾份留在windows作slave
2、啟動master端scrapy,向master的redis中添加url,添加完成后master會繼續運行爬蟲,從redis取url進行抓取,數據存入master mongodb
3、啟動多個slave爬蟲,slave會遠程向master redis中取url采集數據,采集數據會實時存入master mongodb中。
新建一個scrapy項目,完成常規的爬蟲編碼。
開始改動代碼實現分布式爬蟲,首先引入RedisSpider,把原來繼承自scrapy.spider改為繼承RedisSpider。
添加redis_key = 'shixisheng:start_urls' ;這里的redis_key實際上就是一個變量名,master爬蟲爬到的所有URL都會保存到redis中這個名為“readcolorspider:start_urls”的列表下面,slave爬蟲同時也會從這個列表中讀取后續頁面的URL。這個變量名可以任意修改。
修改設置settings.py
①Scheduler,首先是Scheduler的替換,這個東西是Scrapy中的調度員。在settings.py中添加以下代碼:
SCHEDULER="scrapy_redis.scheduler.Scheduler"
②去重
DUPEFILTER_CLASS="scrapy_redis.dupefilter.RFPDupeFilter"
③不清理Redis隊列
SCHEDULER_PERSIST=True
如果這一項為True,那么在Redis中的URL不會被Scrapy_redis清理掉,這樣的好處是:爬蟲停止了再重新啟動,它會從上次暫停的地方開始繼續爬取。但是它的弊端也很明顯,如果有多個爬蟲都要從這里讀取URL,需要另外寫一段代碼來防止重復爬取。
如果設置成了False,那么Scrapy_redis每一次讀取了URL以后,就會把這個URL給刪除。這樣的好處是:多個服務器的爬蟲不會拿到同一個URL,也就不會重復爬取。但弊端是:爬蟲暫停以后再重新啟動,它會重新開始爬。
④設置redis地址
啟用本地redis: REDIS_URL = 'redis://127.0.0.1:6379'
啟用遠程redis: REDIS_URL = 'redis://39.106.155.194:6379'其他設置(可選)
爬蟲請求的調度算法
爬蟲的請求調度算法,有三種情況可供選擇:
①隊列
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderQueue'
如果不配置調度算法,默認就會使用這種方式。它實現了一個先入先出的隊列,先放進Redis的請求會優先爬取。
②棧
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderStack'
這種方式,后放入到Redis的請求會優先爬取。
③優先級隊列
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderPriorityQueue'
這種方式,會根據一個優先級算法來計算哪些請求先爬取,哪些請求后爬取。這個優先級算法比較復雜,會綜合考慮請求的深度等各個因素。
呼~~配置完這些,爬蟲就可以正常工作了,slave從master取url采集數據,當master redis中"shixisheng:start_urls"和"slave_1:requests"都為空時,爬蟲會暫停等待,直到redis中有新的url。若再無新url添加進來,就可以在此刻結束程序。
分布式爬蟲狀態與對應的redis中集合的變化
該聊聊數據存儲的問題了
兩種方法:
- 1.各存各的,master僅提供待爬取url,slave采集后數據存在slave本地,最后把數據匯總即可,這樣一來數據就存在了master一部分,各slave一部分,如果slave比較多,數據的匯總也比較麻煩。
- 2.各個slave采集數據的同時把數據實時的發送到master,這樣數據只存在于master,而slave就充當了真正意義上的“slave”————“干完就走,兩袖清風”
顯然第二種辦法比較好,master即master,slave即slave。
該項目數據庫選用了mongodb。(也實現了存Mysql,下文會講)
針對不同類型數據可以根據具體需求來選擇不同的數據庫存儲。結構化數據可以使用mysql節省空間,非結構化、文本等數據可以采用mongodb等非關系型數據提高訪問速度。具體選擇可以自行百度谷歌,有很多關于sql和nosql的對比文章。
寫入master mongodb代碼(piplines.py)
寫入slave mysql代碼(piplines.py)
上圖是save to slave mysql ,如需save to master請在MySQLStorePipeline更改數據庫連接配置。
settings.py文件
master mongodb部分截圖:
本項目已上傳到github,歡迎友情加star: https://github.com/TimeAshore/scrapy_redis_sxs/
本人也比較小白 學術尚淺,如有什么問題,歡迎提出來共同進步。
參考文章:
http://blog.csdn.net/howtogetout/article/details/51633814
https://www.cnblogs.com/zjl6/p/6742673.html
http://blog.csdn.net/zhou_1997/article/details/52624468
http://python.jobbole.com/86328/
redis基本操作
進入本地redis:
redis-cli (linux)
在Redis安裝目錄運行redis-cli (win)
連接到遠程redis:
redis-cli -h 39.106.155.194 -p 6379 (linux)
在Redis安裝目錄運行redis-cli -h 39.106.155.194 -p 6379 (win)
清空redis:flushdb
查看集合:keys *
轉載需注明原文章地址:http://www.lxweimin.com/p/674d90294f9c