scrapy原始碼分析(六)---------------CrawlProcess
阿新 • • 發佈:2019-02-11
上一篇教程中講到crawl命令最終會執行CrawlProcess的crawl和start方法。這一篇對CrawlProcess的原始碼進行詳細分析,來了解一下是如何進行爬取任務的。
先看一下CrawlProcess的建構函式:
scrapy/crawler.py:
可以看到這個模組一共有3個類:Crawler,CrawlerRunner,CrawlerProcess.
Crawler代表了一種爬取任務,裡面使用一種spider,CrawlerProcess可以控制多個Crawler同時進行多種爬取任務。
CrawlerRunner是CrawlerProcess的父類,CrawlerProcess通過實現start方法來啟動一個Twisted的reactor,並控制shutdown訊號,比如crtl-C,它還配置頂層的logging模組。
下面結合原始碼對原始碼進行註釋解析:
class CrawlerProcess(CrawlerRunner):def __init__(self, settings=None): super(CrawlerProcess, self).__init__(settings) /*使用settings初始化父類CrawlerRunner*/ install_shutdown_handlers(self._signal_shutdown) /*註冊shutdown訊號(SIGINT, SIGTERM等)的處理*/ configure_logging(self.settings) /*配置loggin*/ log_scrapy_info(self.settings) /*記錄scrapy的資訊*/
再分別來看crawl命令最終呼叫的crawl和start函式實現 :
def crawl(self, crawler_or_spidercls, *args, **kwargs):crawler = self.create_crawler(crawler_or_spidercls) /*crawl方法會建立一個Crawler物件,然後呼叫Crawler 的crawl方法開啟一個爬取任務,同時Crawler的crawl方法會返回一個Deferred物件,CrawlerProcess會將這個Deferred物件 加入一個_active集合,然後就可以在必要時結束Crawler,並通過向Deferred中新增_done callback來跟蹤一個Crawler的結束 。*/return self._crawl(crawler, *args, **kwargs) /*用建立的Crawler物件呼叫_crawl方法*/
def create_crawler(self, crawler_or_spidercls):if isinstance(crawler_or_spidercls, Crawler): /*如果已經是一個Crawler例項則直接返回*/ return crawler_or_spidercls return self._create_crawler(crawler_or_spidercls) /*如果crawler_or_spidercls是一個Spider的子類則建立 一個新的Crawler,如果crawler_or_spidercls是一個字串,則根據名稱來查詢對應的spider並建立一個Crawler例項*/def _crawl(self, crawler, *args, **kwargs):
self.crawlers.add(crawler)
d = crawler.crawl(*args, **kwargs) /*呼叫Crawler的crawl方法*/
self._active.add(d)
def _done(result): /*向deferred新增一個callback,如果Crawler已經結束則從活動集合中移除一個Crawler*/ self.crawlers.discard(crawler) self._active.discard(d) return result
return d.addBoth(_done) 這裡還需要再分析的就是Crawler物件的crawl方法: crawl這個函式使用了Twisted的defer.inlineCallbacks裝飾器,表明如果函式中有地方需要阻塞,則不會阻塞整個總流程。 會讓出執行權,關於這個裝飾器的詳細講解請檢視我前面關於Deferred的教程。
@defer.inlineCallbacks def crawl(self, *args, **kwargs): assert not self.crawling, "Crawling already taking place" self.crawling = True try: self.spider = self._create_spider(*args, **kwargs) /*建立一個spider,通過呼叫spider的
from_crawler的方法來建立一個spider物件*/
self.engine = self._create_engine() /*建立一個ExecutionEngine執行引擎*/ start_requests = iter(self.spider.start_requests()) /*獲取spider定義的start_requests,這個在教程四中有詳細 講解*/
yield self.engine.open_spider(self.spider, start_requests) /*呼叫執行引擎開啟spider,關於Execution的原始碼分析將在下 一篇教程中詳解*/
yield defer.maybeDeferred(self.engine.start) /*啟動執行引擎*/
except Exception:
if six.PY2:
exc_info = sys.exc_info()
self.crawling = False
if self.engine is not None:
yield self.engine.close()
if six.PY2:
six.reraise(*exc_info)
raise
現在,還剩CrawlProcess的start函式,原始碼分析如下;
def start(self, stop_after_crawl=True):if stop_after_crawl: d = self.join() # Don't start the reactor if the deferreds are already fired if d.called: return d.addBoth(self._stop_reactor) reactor.installResolver(self._get_dns_resolver()) /*安裝一個dns快取*/ tp = reactor.getThreadPool() tp.adjustPoolsize(maxthreads=self.settings.getint('REACTOR_THREADPOOL_MAXSIZE')) /*根據配置調整 reactor的執行緒池*/ reactor.addSystemEventTrigger('before', 'shutdown', self.stop) reactor.run(installSignalHandlers=False) /*啟動reactor*/這個函式首先呼叫join函式來對前面所有Crawler的crawl方法返回的Deferred物件新增一個_stop_reactor方法,當所有Crawler
物件都結束時用來關閉reactor.