天天看點

Scrapy架構-去重原理講解、資料收集以及信号量知識

scrapy的去重原理

信号無處不在

【知其然且知其是以然,才能夠更好的了解這個架構,而且在使用和改動的時候也能夠想出更合理的方法。】

(開始測試前,到settings.py中注釋掉下載下傳中間件的設定,這裡用jobbole爬蟲來測試,是以之前寫的調用chrome的兩個方法init和spider_closed都要注釋掉。)

這裡你們可以用自己的爬蟲來測試,不一定要按我的來測試。

到scrapy源碼包

[項目\Lib\site-packages\scrapy\dupefilters.py]

裡面找去重的代碼,RFPDupeFilter類就是去重器,裡面有個方法叫做request_seen,它在scheduler(發起請求的第一時間)的時候被調用。它代碼裡面調用了request_fingerprint方法(就是給request生成一個指紋),連續兩次跟進代碼就進入到了request.py檔案的request_fingerprint方法中,方法中有一句代碼:

fp = hashlib.sha1()
…

…

cache[include_headers] = fp.hexdigest()
           

就是給每一個傳遞過來的url生成一個固定長度的唯一的哈希值。這種量級千萬到億的級别記憶體是可以應付的。

然後看到init方法:

def __init__(self, path=None, debug=False):
        self.file = None
        self.fingerprints = set()
        self.logdupes = True
        self.debug = debug
        self.logger = logging.getLogger(__name__)
        if path:
            self.file = open(os.path.join(path, 'requests.seen'), 'a+')
            self.file.seek(0)
            self.fingerprints.update(x.rstrip() for x in self.file)

           

裡面有一句代碼 self.fingerprints = set(),就是通過set集合的特點(set不允許有重複值)進行去重。

可以用斷點調試的方法進行跟蹤檢視。

Telnet

Telnet協定是TCP/IP協定族中的一員,是Internet遠端登陸服務的标準協定和主要方式。它為使用者提供了在本地計算機上完成遠端主機工作的能力。在終端使用者的電腦上使用telnet程式,用它連接配接到伺服器。終端使用者可以在telnet程式中輸入指令,這些指令會在伺服器上運作,就像直接在伺服器的控制台上輸入一樣。可以在本地就能控制伺服器。要開始一個telnet會話,必須輸入使用者名和密碼來登入伺服器。Telnet是常用的遠端控制Web伺服器的方法。

可以用這個在本地操控遠端的scrapy,telnet是預設開啟的,當scrapy運作的時候,會自動開啟端口,運作框會有顯示:

[scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
           

我們可以通過cmd連接配接測試。

要測試,就要開啟windows電腦的telnet功能。在控制台-程式與功能-啟用或關閉windows功能,找到Telnet服務和用戶端,打上勾即可。

linux系統也是預設沒有Telnet的,需要安裝,比如我的Deepin系統就需要用指令安裝:

sudo apt-get install telnet
           

先運作scrap有,然後在cmd(linux在終端輸入)中輸入:

telnet localhost 6023
           

連接配接成功後會顯示:

>>>
           

符号,我們輸入est()可以檢視目前scrapy的運作狀态等屬性。

官方文檔

有Telnet的相關介紹,裡面包括有一些可用的變量/檢視引擎狀态/暫停,恢複和停止scrapy/終端信号/設定等内容。

Telnet的源碼在site-package/scrapy/extensions目錄下的telnet.py檔案中。

資料收集器

官方文檔中有對

的介紹.

資料收集器可以應用在很多地方,舉例子:如果你想知道scrapy總共發出了多少個request請求;或者你想記錄總共發起了多少次yeild,都可以用資料收集器記錄,它不用打開,預設可以直接使用。

在jobbole爬蟲的JobboleSpider類裡面新增代碼:

# 收集jobbole.com所有的500、404頁面及頁面數量
    handle_httpstatus_list = [500,404]
    def __init__(self):
        self.fail_urls = []

           

然後到parse方法中新增代碼:

if response.status == 500 or response.status == 404:
            self.fail_urls.append(response.url)
            self.crawler.stats.inc_value("failed_url")
           

就可以實作對頁面數量和頁面url的收集,可以自定義儲存.

信号

scrapy的中間件與通信都是通過信号來傳遞的,

官網有文檔

可以在您的Scrapy項目中捕捉一些信号(使用 extension)來完成額外的工作或添加額外的功能,擴充Scrapy。

信号提供了一些參數,不過處理函數不用接收所有的參數 - 信号分發機制(singal dispatching mechanism)僅僅提供處理器(handler)接受的參數。

代碼示範:

from scrapy.xlib.pydispatch import dispatcher
from scrapy import signals

    def parse(self, response):
        """ 正式進入爬取區域 """
        dispatcher.connect(self.handler_spider_closed, signals.spider_closed)

    def handler_spider_closed(self, spider, reason):
        print("這個名為:" + spider.name + "的爬蟲已經關閉了,原因是:" + reason)
           

得到的輸出結果是在爬蟲關閉後:

'start_time': datetime.datetime(2018, 1, 20, 3, 5, 49, 791339)}
2018-01-20 11:05:51 [scrapy.core.engine] INFO: Spider closed (finished)

這個名為:dongmeng的爬蟲已經關閉了,原因是:finished
           

dispatcher.connect監聽爬蟲signals(信号),當收到爬蟲關閉(signals.spider_closed)的信号時,調用handler_spider_closed方法。而handler_spider_closed我隻是簡單的編寫了一個關閉的原因而已,還可以做更深入的操作。

由此可以看出,爬蟲的信号監聽和狀态操作可以做很多的事情,比如打開爬蟲時、爬蟲空閑時可以收集目前request請求隊列、記錄404頁面數量或者200狀态的頁面有那些、多少條等。

擴充

對擴充有介紹:

擴充架構提供一個機制,使得你能将自定義功能綁定到Scrapy。

擴充隻是正常的類,它們在Scrapy啟動時被執行個體化、初始化。

scrapy裡面的中間件都是一種擴充。

為了更好的了解擴充,這裡用源碼跟蹤的形式來了解,到[項目/Lib/site-packages/scrapy/extensions]目錄下找到corestats.py檔案。

from_crawler方法裡面記錄了很多的信号量:

@classmethod
    def from_crawler(cls, crawler):
        o = cls(crawler.stats)
        crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(o.spider_closed, signal=signals.spider_closed)
        crawler.signals.connect(o.item_scraped, signal=signals.item_scraped)
        crawler.signals.connect(o.item_dropped, signal=signals.item_dropped)
        crawler.signals.connect(o.response_received, signal=signals.response_received)
        return o
           

下面對應都寫着具體的執行方法。比如spider_opened爬蟲啟動的時候就會:

def spider_opened(self, spider):
        self.stats.set_value('start_time', datetime.datetime.utcnow(), spider=spider)
           

記錄爬蟲啟動的時間。

還有spider_closed關閉爬蟲的時候就會:

def spider_closed(self, spider, reason):
        self.stats.set_value('finish_time', datetime.datetime.utcnow(), spider=spider)
        self.stats.set_value('finish_reason', reason, spider=spider)
           

記錄下關閉時間和關閉的原因等。

到[項目/Lib/site-packages/scrapy/extensions]memusage.py檔案是監控記憶體使用的。裡面有一串代碼:

crawler.signals.connect(self.engine_started, signal=signals.engine_started)
        crawler.signals.connect(self.engine_stopped, signal=signals.engine_stopped)
           

是主要的綁定項

比如這個engine_started方法開始就記錄記憶體使用資訊。

self.crawler = crawler
        self.warned = False
        self.notify_mails = crawler.settings.getlist('MEMUSAGE_NOTIFY_MAIL')
        self.limit = crawler.settings.getint('MEMUSAGE_LIMIT_MB')*1024*1024
        self.warning = crawler.settings.getint('MEMUSAGE_WARNING_MB')*1024*1024
        self.check_interval = crawler.settings.getfloat('MEMUSAGE_CHECK_INTERVAL_SECONDS')
        self.mail = MailSender.from_settings(crawler.settings)
           

裡面的大概意思就是設定了監控記憶體的定時時間/不同狀态的操作等,可以在engine_started裡面加邏輯,想對記憶體幹什麼就幹什麼。