CrawlerProcess主進程
它控制了twisted的reactor,也就是整個事件循環。它負責配置reactor並啟動事件循環,最后在所有爬取結束后停止reactor。
另外還控制了一些信號操作,使用戶可以手動終止爬取任務。
此類在scrapy/crawler.py中定義,此模塊有三個類:Crawler、CrawlerRunner和CrawlerProcess。
Crawler代表了一種爬取任務,里面使用一種spider,CrawlerProcess可以控制多個Crawler同時進行多種爬取任務。
CrawlerRunner是CrawlerProcess的父類,CrawlerProcess通過實現start方法來啟動一個Twisted的reactor(另有shutdown信號處理、頂層logging功能)。
CrawlerProcess初始化
首先在命令行啟動調用crawl()和start()運行之前,就已經建立了CrawlerProcess對象。
scrapy/crawler.py#CrawlerProcess:
class CrawlerProcess(CrawlerRunner): def __init__(self, settings=None, install_root_handler=True): super(CrawlerProcess, self).__init__(settings) install_shutdown_handlers(self._signal_shutdown) configure_logging(self.settings, install_root_handler) log_scrapy_info(self.settings)
初始化動作有:
1.使用settings初始化父類CrawlerRunner,只是定義了一些空變量。
2.注冊shutdown信號。
3.配置頂層logging。
CrawlerProcess.crawl()創建Crawler對象
在運行前調用了crawl()方法。
scrapy/crawler.py#CrawlerRunner:

def crawl(self, crawler_or_spidercls, *args, **kwargs): crawler = self.create_crawler(crawler_or_spidercls) return self._crawl(crawler, *args, **kwargs) def _crawl(self, crawler, *args, **kwargs): self.crawlers.add(crawler) d = crawler.crawl(*args, **kwargs) self._active.add(d) def _done(result): self.crawlers.discard(crawler) self._active.discard(d) return result return d.addBoth(_done) def create_crawler(self, crawler_or_spidercls): if isinstance(crawler_or_spidercls, Crawler): return crawler_or_spidercls return self._create_crawler(crawler_or_spidercls) def _create_crawler(self, spidercls): if isinstance(spidercls, six.string_types): spidercls = self.spider_loader.load(spidercls) return Crawler(spidercls, self.settings)
這里需要分清crawler對象和spider對象:
spider是程序員編寫的爬蟲代碼模塊,一般是存放在項目里spiders文件夾內,並給每個爬蟲模塊賦予獨立的名稱,命令行啟動時通過不同的名稱啟動不同的spider;
crawler是爬取任務,每次在命令行啟動,都會新建一個新的crawler爬取任務,可以為同一個spider新建多個crawler,表現在命令里就是同樣的命令可以重復執行多次,同一個spider對應的多個crawler共同占有同樣的私有配置、同一個任務隊列。
crawl()方法執行的動作有:
1.如果crawler_or_spidercls(命令行輸入的spider名稱)是一個Spider的子類(已經運行)則創建一個新的Crawler,如果crawler_or_spidercls是一個字符串(未運行),則根據名稱來查找對應的spider並創建一個Crawler實例並執行Crawler的初始化。
2.返回一個Deferred對象給CrawlerProcess,把Deferred對象加入_active集合,然后就可以在必要時結束Crawler,並通過向Deferred中添加_done callback來跟蹤一個Crawler的結束。
Crawler對象初始化
scrapy/crawler.py#Crawler:
class Crawler(object): def __init__(self, spidercls, settings=None): if isinstance(settings, dict) or settings is None: settings = Settings(settings) self.spidercls = spidercls self.settings = settings.copy() self.spidercls.update_settings(self.settings) d = dict(overridden_settings(self.settings)) logger.info("Overridden settings: %(settings)r", {'settings': d}) self.signals = SignalManager(self) self.stats = load_object(self.settings['STATS_CLASS'])(self) handler = LogCounterHandler(self, level=self.settings.get('LOG_LEVEL')) logging.root.addHandler(handler) if get_scrapy_root_handler() is not None: # scrapy root handler already installed: update it with new settings install_scrapy_root_handler(self.settings) # lambda is assigned to Crawler attribute because this way it is not # garbage collected after leaving __init__ scope self.__remove_handler = lambda: logging.root.removeHandler(handler) self.signals.connect(self.__remove_handler, signals.engine_stopped) lf_cls = load_object(self.settings['LOG_FORMATTER']) self.logformatter = lf_cls.from_crawler(self) self.extensions = ExtensionManager.from_crawler(self) self.settings.freeze() self.crawling = False self.spider = None self.engine = None
動作:
1.導入spider的custom_settings;
2.添加SignalManager(利用開源的python庫pydispatch作消息的發送和路由, scrapy使用它發送關鍵的消息事件給關心者,如爬取開始,爬取結束等消息。通過send_catch_log_deferred來發送消息,通過connect方法來注冊消息的處理函數);
3.添加ExtensionManager。
Crawler.crawl()創建spider對象
scrapy/crawler.py#Crawler:

@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) self.engine = self._create_engine() start_requests = iter(self.spider.start_requests()) yield self.engine.open_spider(self.spider, start_requests) 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
調用Crawler的crawl方法(這里使用了Twisted的defer.inlineCallbacks裝飾器,表明此函數非阻塞,異步執行)開啟一個爬取任務,通過調用spider的from_crawler方法來創建一個spider對象,這樣,許多spider類都可以使用crawler的關鍵方法和數據,屬於依賴注入。
spider的代碼由程序員自己編寫,不同的爬蟲類除了調用父類的from_crawler外,可以重定義這個方法來實現個性化實現。
典型寫法為:
@classmethod def from_crawler(cls, crawler): s = cls() crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) return s
Crawler.crawl()創建ExecutionEngine執行引擎以及對應的調度器、下載器、刮取器
繼續向下執行:
1.self.engine = self._create_engine(),創建一個ExecutionEngine執行引擎;
ExecutionEngine的初始化代碼為:
scrapy/core/engine.py#ExecutionEngine:
class ExecutionEngine(object): def __init__(self, crawler, spider_closed_callback): self.crawler = crawler self.settings = crawler.settings self.signals = crawler.signals self.logformatter = crawler.logformatter self.slot = None self.spider = None self.running = False self.paused = False self.scheduler_cls = load_object(self.settings['SCHEDULER']) downloader_cls = load_object(self.settings['DOWNLOADER']) self.downloader = downloader_cls(crawler) self.scraper = Scraper(crawler) self._spider_closed_callback = spider_closed_callback
操作有:使用crawler的信號管理器,用來發送注冊消息;根據配置加載調度類模塊,默認是scrapy.core.scheduler.Scheduler;根據配置加載下載類模塊,並創建一個對象,默認是scrapy.core.downloader.Downloade;創建一個Scraper刮取器,主要是用來處理下載后的結果並存儲提取的數據。
2.start_requests = iter(self.spider.start_requests()),獲取spider中的start_requests;
3.yield self.engine.open_spider(self.spider, start_requests),調用執行引擎打開spider;
4.yield defer.maybeDeferred(self.engine.start),啟動執行引擎。此時仍然並未真正開始爬取,仍然是CrawlerProcess.start()之前的預處理步驟。
ExecutionEngine的詳細內容將在下一篇講解。
CrawlerProcess.start()
正式運行。
scrapy/crawler.py#CrawlerProcess:
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()) tp = reactor.getThreadPool() tp.adjustPoolsize(maxthreads=self.settings.getint('REACTOR_THREADPOOL_MAXSIZE')) reactor.addSystemEventTrigger('before', 'shutdown', self.stop) reactor.run(installSignalHandlers=False) # blocking call
1.此函數首先調用join函數來對前面所有Crawler的crawl方法返回的Deferred對象添加一個_stop_reactor方法,當所有Crawler對象都結束時用來關閉reactor。
2.reactor.installResolver安裝一個dns緩存。
3.根據配置調整reactor的線程池。
4.添加結束事件的監聽。
5.啟動reactor事件循環,標志着所有爬蟲正式運行,如果沒有手動結束,就只會在所有爬蟲全部爬取完成后才會自動結束。
————————————————
版權聲明:本文為CSDN博主「csdn_yym」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/csdn_yym/java/article/details/85423656