2021SC@SDUSC
3.2 从入口开始分析简单的认识了主要的几个类之后,我们来跟下代码。运行爬虫时,很多时候我们会这么执行
$ scrapy crawl
看下这个 scrapy 可执行文件
#!/Users/kevinbai/.pyenv/versions/3.7.5/envs/site_analysis/bin/python3.7
# -*- coding: utf-8 -*-
import re
import sys
from scrapy.cmdline import execute
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script.pyw?|.exe)?$', '', sys.argv[0])
sys.exit(execute())
PS:使用不同管理工具,可执行文件的路径有差别,上面的虚拟环境是由 pyenv 创建,你要根据自己的情况去找下。
原来就是调用 scrapy.cmdline.execute 方法
# scrapy/cmdline.py
def execute(argv=None, settings=None):
...
cmd.crawler_process = CrawlerProcess(settings)
_run_print_help(parser, _run_command, cmd, args, opts)
sys.exit(cmd.exitcode)
这个方式是命令行的入口,根据项目配置做了一些基础的配置,并使用 CrawlerProcess 作为爬虫的执行环境。看下 crawl 命令
# scrapy/commands/crawl.py
class Command(ScrapyCommand):
...
def run(self, args, opts):
...
self.crawler_process.crawl(spname, **opts.spargs)
self.crawler_process.start()
if self.crawler_process.bootstrap_failed:
self.exitcode = 1
这里调用了 CrawlerProcess 的 crawl 和 start 方法。先看下后者
# scrapy/crawler.py
class CrawlerProcess(CrawlerRunner):
...
def start(self, stop_after_crawl=True):
...
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
做了这么几件事
- 自定义 DNS 解析器
- 根据配置调整线程池,这个线程池将会用于解析 DNS,因为 Scrapy 解析 DNS 时,会用到 socket 库,只能使用多线程进行并发
- 绑定回调,当程序要结束时停止当前运行的爬虫
- 启动 reactor
至于爬虫是怎么创建的,引擎内部是怎么调度的,就要从 crawl 方法开始看了
# scrapy/crawler.py
class Crawler(object):
...
@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:
...
- 首先是创建爬虫 spider
- 然后将 spider 和初始请求的迭代器作为参数创建引擎 engine
- 执行 engine 的 open_spider 方法并启动 engine
这部分代码我先不分析代码里面关于错误的解决,这有助于理解代码的核心思路。



