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裡面加邏輯,想對記憶體幹什麼就幹什麼。