上一篇教程中讲到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模块。
    image.png
    再分别来看crawl命令最终调用的crawl和start函数实现 :

    1. def crawl(self, crawler_or_spidercls, *args, **kwargs):
    2. """
    3. Run a crawler with the provided arguments.
    4. crawl方法会创建一个Crawler对象,然后调用Crawler的crawl方法开启一个爬取任务,
    5. 同时Crawler的crawl方法会返回一个Deferred对象,CrawlerProcess会将这个
    6. Deferred对象加入一个_active集合,然后就可以在必要时结束Crawler,
    7. 并通过向Deferred中添加_done callback来跟踪一个Crawler的结束。
    8. """
    9. crawler = self.create_crawler(crawler_or_spidercls)
    10. return self._crawl(crawler, *args, **kwargs)
    11. def _crawl(self, crawler, *args, **kwargs):
    12. self.crawlers.add(crawler)
    13. d = crawler.crawl(*args, **kwargs) # 调用Crawler的crawl方法
    14. self._active.add(d)
    15. def _done(result):
    16. """
    17. 向deferred添加一个callback,如果Crawler已经结束
    18. 则从活动集合中移除一个Crawler
    19. """
    20. self.crawlers.discard(crawler)
    21. self._active.discard(d)
    22. self.bootstrap_failed |= not getattr(crawler, 'spider', None)
    23. return result
    24. return d.addBoth(_done)
    25. def create_crawler(self, crawler_or_spidercls):
    26. """
    27. Return a :class:`~scrapy.crawler.Crawler` object.
    28. """
    29. if isinstance(crawler_or_spidercls, Spider):
    30. raise ValueError(
    31. 'The crawler_or_spidercls argument cannot be a spider object, '
    32. 'it must be a spider class (or a Crawler object)')
    33. if isinstance(crawler_or_spidercls, Crawler): # 如果已经是一个Crawler实例则直接返回
    34. return crawler_or_spidercls
    35. return self._create_crawler(crawler_or_spidercls)
    36. def _create_crawler(self, spidercls):
    37. if isinstance(spidercls, str):
    38. # 如果crawler_or_spidercls是一个字符串,则根据名称来查找对应的spider并创建一个Crawler实例
    39. spidercls = self.spider_loader.load(spidercls)
    40. return Crawler(spidercls, self.settings)

    这里还需要再分析的就是Crawler对象的crawl方法:

    crawl这个函数使用了Twisted的defer.inlineCallbacks装饰器,表明如果函数中有地方需要阻塞,则不会阻塞整个总流程,会让出执行权。
    image.png
    现在,还剩CrawlProcess的start函数,源码分析如下:
    image.png
    这个函数首先调用join函数来对前面所有Crawler的crawl方法返回的Deferred对象添加一个_stop_reactor方法,当所有Crawler对象都结束时用来关闭reactor。