最近在工做中寫了不少 scrapy_redis 分佈式爬蟲,可是回想 scrapy 與 scrapy_redis 二者區別的時候,居然,思惟只是侷限在了應用方面,因而乎,搜索了不少相關文章介紹,這才搞懂內部實現的原理。python
首先咱們從總體上來說react
scrapy是一個Python爬蟲框架,爬取效率極高,具備高度定製性,可是不支持分佈式。而scrapy-redis一套基於redis數據庫、運行在scrapy框架之上的組件,可讓scrapy支持分佈式策略,Slaver端共享Master端redis數據庫裏的item隊列、請求隊列和請求指紋集合。而爲何選擇redis數據庫,是由於redis支持主從同步,並且數據都是緩存在內存中的,因此基於redis的分佈式爬蟲,對請求和數據的高頻讀取效率很是高。web
有一篇文章是這麼說的:scrapy-redis
與 Scrapy
的關係就像電腦與固態硬盤同樣,是電腦中的一個插件,能讓電腦更快的運行。redis
Scrapy
是一個爬蟲框架,scrapy-redis
則是這個框架上能夠選擇的插件,它可讓爬蟲跑的更快。數據庫
說的一點都對,Scrapy
是一個通用的爬蟲框架,scrapy-redis
則是這個框架上能夠選擇的插件,爲了更方便地實現Scrapy分佈式爬取,而提供了一些以redis爲基礎的組件(僅有組件),它可讓爬蟲跑的更快。
緩存
而後介紹 scrapy 框架的運行流程及原理服務器
scrapy做爲一款優秀的爬蟲框架,在爬蟲方面有這衆多的優勢。能快速、高層次的屏幕抓取和web抓取框架,用於抓取web站點並從頁面中提取結構化的數據。架構
爲了方便理解,我找到了一張這樣的圖片:框架
解釋說明:異步
一、從優先級隊列中獲取request對象,交給engine
二、engine將request對象交給下載器下載,期間會經過downloadmiddleware的process_request方法
三、下載器完成下載,得到response對象,將該對象交給engine,期間會通過downloadmiddleware的process_response( )方法
四、engine將得到的response對象交給spider進行解析,期間會通過spidermiddleware的process_spider_input()的方法
五、spider解析下載器下下來的response,返回item或是links(url)
六、item或者link通過spidermiddleware的process_spider_out( )方法,交給engine
七、engine將item交給item pipeline ,將links交給調度器
八、在調度器中,先將requests對象利用scrapy內置的指紋函數生成一個指紋
九、若是requests對象中的don't filter參數設置爲False,而且該requests對象的指紋不在信息指紋的隊列中,那麼就把該request對象放到優先級隊列中
循環以上操做
中間件主要存在兩個地方,從圖片當中咱們能夠看到:
spider 與 engine 之間(爬蟲中間件):
介於Scrapy引擎和爬蟲之間的框架,主要工做是處理爬蟲的響應輸入和請求輸出
download 與 engine 之間(下載器中間件) :
位於Scrapy引擎和下載器之間的框架,主要是處理Scrapy引擎與下載器之間的請求及響應
藉此機會,咱們結合程序,解析一下框架中的 middleware.py :
1. Spider Middleware有如下幾個函數被管理:
- process_spider_input 接收一個response對象並處理,
位置是Downloader-->process_spider_input-->Spiders(Downloader和Spiders是scrapy官方結構圖中的組件)
- process_spider_exception spider出現的異常時被調用
- process_spider_output 當Spider處理response返回result時,該方法被調用
- process_start_requests 當spider發出請求時,被調用
2. Downloader Middleware有如下幾個函數被管理
- process_request request經過下載中間件時,該方法被調用,這裏能夠設置代理,設置request.meta['proxy'] 就行
- process_response 下載結果通過中間件時被此方法處理
- process_exception 下載過程當中出現異常時被調用
我的理解scrapy的優缺點:
優勢:scrapy 是異步的,寫middleware,方便寫一些統一的過濾器
缺點:基於python的爬蟲框架,擴展性比較差,基於twisted框架,運行中的exception是不會幹掉reactor,而且異步框架出錯後是不會停掉其餘任務的,數據出錯後難以察覺。
scrapy_redis分佈式爬蟲
最後回到咱們這篇文章的重點(敲黑板...)
Scrapy-redis提供了下面四種組件(components):(四種組件意味着這四個模塊都要作相應的修改)
Scheduler:
Scrapy改造了python原本的collection.deque(雙向隊列)造成了本身的Scrapy queue,可是Scrapy多個spider不能共享待爬取隊列Scrapy queue, 即Scrapy自己不支持爬蟲分佈式,scrapy-redis 的解決是把這個Scrapy queue換成redis數據庫(也是指redis隊列),從同一個redis-server存放要爬取的request,便能讓多個spider去同一個數據庫裏讀取。
Scrapy中跟「待爬隊列」直接相關的就是調度器Scheduler,它負責對新的request進行入列操做(加入Scrapy queue),取出下一個要爬取的request(從Scrapy queue中取出)等操做。它把待爬隊列按照優先級創建了一個字典結構,而後根據request中的優先級,來決定該入哪一個隊列,出列時則按優先級較小的優先出列。爲了管理這個比較高級的隊列字典,Scheduler須要提供一系列的方法。可是原來的Scheduler已經沒法使用,因此使用Scrapy-redis的scheduler組件。
Duplication Filter:
Scrapy中用集合實現這個request去重功能,Scrapy中把已經發送的request指紋放入到一個集合中,把下一個request的指紋拿到集合中比對,若是該指紋存在於集合中,說明這個request發送過了,若是沒有則繼續操做。
在scrapy-redis中去重是由Duplication Filter組件來實現的,它經過redis的set 不重複的特性,巧妙的實現了Duplication Filter去重。scrapy-redis調度器從引擎接受request,將request的指紋存⼊redis的set檢查是否重複,並將不重複的request push寫⼊redis的 request queue。
引擎請求request(Spider發出的)時,調度器從redis的request queue隊列⾥里根據優先級pop 出⼀個request 返回給引擎,引擎將此request發給spider處理。
Item Pipeline:
引擎將(Spider返回的)爬取到的Item給Item Pipeline,scrapy-redis 的Item Pipeline將爬取到的 Item 存⼊redis的 items queue。
修改過Item Pipeline能夠很方便的根據 key 從 items queue 提取item,從⽽實現items processes集羣。
Base Spider:
不在使用scrapy原有的Spider類,重寫的RedisSpider繼承了Spider和RedisMixin這兩個類,RedisMixin是用來從redis讀取url的類。
當咱們生成一個Spider繼承RedisSpider時,調用setup_redis函數,這個函數會去鏈接redis數據庫,而後會設置signals(信號):
一個是當spider空閒時候的signal,會調用spider_idle函數,這個函數調用schedule_next_request函數,保證spider是一直活着的狀態,而且拋出DontCloseSpider異常。
一個是當抓到一個item時的signal,會調用item_scraped函數,這個函數會調用schedule_next_request函數,獲取下一個request。
Scrapy-redis架構:
如上圖所示,咱們能夠發現,scrapy-redis在scrapy的架構上增長了redis,與scrapy相差無幾。本質的區別就是,將scrapy的內置的去重的隊列和待抓取的request隊列換成了redis的集合。就這一個小小的改動,就使得了scrapy-redis支持了分佈式抓取。
Scrapy-Redis分佈式策略:
假設有四臺電腦:Windows 十、Mac OS X、Ubuntu 16.0四、CentOS 7.2,任意一臺電腦均可以做爲 Master端 或 Slaver端,好比:
--Master端(核心服務器) :使用 Windows 10,搭建一個Redis數據庫,不負責爬取,只負責url指紋判重、Request的分配,以及數據的存儲
--Slaver端(爬蟲程序執行端) :使用 Mac OS X 、Ubuntu 16.0四、CentOS 7.2,負責執行爬蟲程序,運行過程當中提交新的Request給Master
首先Slaver端從Master端拿任務(Request、url)進行數據抓取,Slaver抓取數據的同時,產生新任務的Request便提交給 Master 處理;
Master端只有一個Redis數據庫,負責將未處理的Request去重和任務分配,將處理後的Request加入待爬隊列,而且存儲爬取的數據。
明白了原理以後咱們就要入手程序了
Scrapy-Redis默認使用的就是這種策略,咱們實現起來很簡單,由於任務調度等工做Scrapy-Redis都已經幫咱們作好了,咱們只須要繼承RedisSpider、指定redis_key就好了。
將 scrapy 變成 scrapy-redis 的過程(前提是pip install scrapy-redis)
一、修改settings.py文件,最簡單的方式是使用redis
替換機器內存,你只須要在 settings.py
中加上三代碼,就能讓你的爬蟲變爲分佈式。
二、配置redis
三、配置以下參數
四、自定義爬蟲類繼承RedisSpider
若是你如今運行你的爬蟲,你能夠在redis中看到出現了這兩個key:
格式是set,即不會有重複數據。前者就是redis的去重隊列,對應DUPEFILTER_CLASS
,後者是redis的請求調度,把裏面的請求分發給爬蟲,對應SCHEDULER
。(裏面的數據不會自動刪除,若是你第二次跑,須要提早清空裏面的數據)
備註:
雖然scrapy_redis 能夠極大的提升爬蟲的運行的效率,但也是存在缺點的,Scrapy-Redis調度的任務是Request對象,裏面信息量比較大(不只包含url,還有callback函數、headers等信息),可能致使的結果就是會下降爬蟲速度、並且會佔用Redis大量的存儲空間,因此若是要保證效率,那麼就須要必定硬件水平。最後提醒一下你們,並非全部的網站均可以採用分佈式爬蟲進行採集,爬蟲要追求靈活,因此scrapy-redis並不能代替傳統的request爬蟲。
部分文字來自:https://blog.csdn.net/weixin_42825585/article/details/88046328