scrapy启动过程源码分析
2016-06-23 19:44
344 查看
看了上一节分析的
(1) Engine:scrapy/scrapy/core/engine.py
(2) Scheduler:scrapy/scrapy/core/scheduler.py
(3) Downloader:scrapy/scrapy/core/downloader/
(4) Spider:scrapy/scrapy/spider.py
(5) Item pipeline:scrapy/scrapy/pipelines/
这些模块,不是用class实现的,就是用package实现的。看模块代码顶多能了解它们的功能(成员函数),根本看不出scrapy是怎么运行的。那么问题来了,scrapy是怎么被启动并运行的?
那这条命令到底做了哪些事呢?
scrapy程序的源代码结构是非常清晰的,可以“猜”到
看样子是用command模式写的,命令会执行
只要能找到
继续搜
通过上面的分析,scrapy程序的启动,就是执行了
(1) 初始化一个Twisted
(2) 调整这个
(3) 创建一个DNS缓存,并调整缓存大小
(4) 判断是否有其他爬虫没跑结束的,必须等所有爬虫跑结束才启动该爬虫
(5) 然后运行
然后这个
在这个
代码跟到这里,必须要看一下
Twisted
而
而最重要的是,
总结一下,
能做啥事呢?也就是
这个类的初始化,主要代码就是下面两行:
加载配置,并根据配置加载
它之所以叫xxxProcess,作者想说的就是“单进程”的意思吧。
这个类除了实现reactor模式,还添加了
(1) 加载用户配置,加载所有
(2) 初始化
(3) 运行
(3.1) 初始化一个Twisted
(3.2) 创建一个DNS缓存,并调整缓存大小
(3.3) 判断是否有其他爬虫没跑结束的,必须等所有爬虫跑结束才启动当前爬虫
(4) 然后运行
每个
(2) twisted.internet.reactor,https://twistedmatrix.com/documents/current/api/twisted.internet.reactor.html
(3) https://likebeta.gitbooks.io/twisted-intro-cn/content/zh/p03.html
(4) scrapy的运行,http://doc.scrapy.org/en/latest/intro/tutorial.html#what-just-happened-under-the-hood
scrapy架构,我们最想了解的,应该就是5大核心模块是怎么实现的吧。好,从github中找到各大核心模块的源码:
(1) Engine:scrapy/scrapy/core/engine.py
(2) Scheduler:scrapy/scrapy/core/scheduler.py
(3) Downloader:scrapy/scrapy/core/downloader/
(4) Spider:scrapy/scrapy/spider.py
(5) Item pipeline:scrapy/scrapy/pipelines/
这些模块,不是用class实现的,就是用package实现的。看模块代码顶多能了解它们的功能(成员函数),根本看不出scrapy是怎么运行的。那么问题来了,scrapy是怎么被启动并运行的?
一个scrapy程序的运行过程
从(1)中我们可以看到,我们自己写一个scrapy程序,这个程序的运行,是从命令scrapy crawl hello开始的。
那这条命令到底做了哪些事呢?
crawl
命令的运行
scrapy程序的源代码结构是非常清晰的,可以“猜”到crawl命令的代码在这里:
https://github.com/scrapy/scrapy/blob/master/scrapy/commands/crawl.py
看样子是用command模式写的,命令会执行
run()中的代码,去掉异常处理部分,其实
crawl命令最核心的代码就是下面两条:
self.crawler_process.crawl(spname, **opts.spargs) self.crawler_process.start()
只要能找到
self.crawler_process.start()的代码,就能搞清楚
crawl命令的运行细节了。要搞明白这两条代码做了什么,必须知道
crawler_process是什么。在github中搜一下这个
crawler_process,发现
cmd.crawler_process = CrawlerProcess(settings)
继续搜
CrawlerProcess(github的搜索功能用起来真是爽)。终于在
scrapy/scrapy/crawler.py里发现了我们需要寻找的
CrawlerProcess.start()函数。
CrawlerProcess.start()
的逻辑
通过上面的分析,scrapy程序的启动,就是执行了self.crawler_process.start(),跟进去发现其实是调用了
CrawlerProcess.start()函数。从代码注释,可看到这个函数做了那么几件事:
(1) 初始化一个Twisted
reactor对象
(2) 调整这个
reactor对象的线程池大小
(3) 创建一个DNS缓存,并调整缓存大小
(4) 判断是否有其他爬虫没跑结束的,必须等所有爬虫跑结束才启动该爬虫
(5) 然后运行
reactor.run(installSignalHandlers=False)
然后这个
reactor就run了,嗯?!这个
reactor是什么鸟,它run啥内容了呢?
在这个
crawler.py文件中看了一圈,也没发现往
reactor中传入什么handler之类的参数。不过倒是发现shutdown/kill一个reactor是很容易的。
代码跟到这里,必须要看一下
Twisted框架的
reactor对象运行机制是怎么样的了。
Twisted reactor
对象
Twisted是python的异步网络编程框架。作为一个框架,它有自己的编程套路。这个套路,就是传说中的“异步编程模式/事件驱动模式”。 事件驱动的特点是包含一个事件循环(loop),当外部事件发生时用回调机制来触发相应的事件处理代码。
而
reactor,就是I/O并发模型中“reactor模式”的实现。从(3)中可知,
reactor就实现了这个事件环(loop)。
而最重要的是,
reactor模式,实现了单线程环境中,调度多个事件源产生的事件到它们各自的事件处理例程中去。借助这个reactor模式,用单线程,就实现了事件处理机制(callback)。
总结一下,
reactor只是一种设计模式,就是一个代码框架而已。真正的代码逻辑,应该在调用
reactor.run()之前就搞定了的。所以想弄明白
CrawlerProcess.start(),得看这个函数调用之前,做了哪些事。
能做啥事呢?也就是
CrawlerProcess类,以及它父类的初始化。
CrawlerProcess类
CrawlerProcess类的父类是
CrawlerRunner类。看看它们的初始化工作都做了啥。
CrawlerRunner类
看它的注释,“keeps track of, manages and runs crawlers inside an already setup Twistedreactor”。看到了吧,它就是管理
reactor中的各个爬虫的。
这个类的初始化,主要代码就是下面两行:
self.settings = settings self.spider_loader = _get_spider_loader(settings)
加载配置,并根据配置加载
spider。所谓加载
spider,从
scrapy/scrapy/spiderloader.py可以看出,就是加载各个
sipder的属性(name等等)。
CrawlerProcess类
父类初始化后,子类CrawlerProcess才执行初始化。而
CrawlerProcess类的功能,根据注释:run multiple scrapy crawlers in a process simultaneously。就是在单进程中跑多个爬虫(用twisted的reactor实现)。这个类里就实现了scrapy的“异步编程模式/事件驱动模式”。
它之所以叫xxxProcess,作者想说的就是“单进程”的意思吧。
这个类除了实现reactor模式,还添加了
log,
shutdown信号处理(Ctrl+C)功能。
总结
理一下,当我们运行scrapy crawl hello命令,就启动了一个scrapy爬虫,它的启动过程是这样的:
(1) 加载用户配置,加载所有
spider(父类
CrawlerRunner类初始化)
(2) 初始化
log,与
shutdown(Ctrl+C)信号处理机制(子类
CrawlerProcess类初始化)
(3) 运行
CrawlerProcess.start(),依次完成下面的逻辑:
(3.1) 初始化一个Twisted
reactor对象,调整这个
reactor对象的线程池大小
(3.2) 创建一个DNS缓存,并调整缓存大小
(3.3) 判断是否有其他爬虫没跑结束的,必须等所有爬虫跑结束才启动当前爬虫
(4) 然后运行
reactor.run()。至此,第一步加载的
spider,都放在reactor模式中运行了。
每个
spider有自己的
name,
start_urls等属性。根据(4),scrapy会为每一个URL创建
scrapy.Request对象,并将
spider的
parse()方法作为
scrapy.Request对象的
callback函数。
参考
(1) 简单scrapy程序的运行,https://github.com/ybdesire/WebLearn/tree/master/23_scrapy/hello_scrapy(2) twisted.internet.reactor,https://twistedmatrix.com/documents/current/api/twisted.internet.reactor.html
(3) https://likebeta.gitbooks.io/twisted-intro-cn/content/zh/p03.html
(4) scrapy的运行,http://doc.scrapy.org/en/latest/intro/tutorial.html#what-just-happened-under-the-hood
相关文章推荐
- ubuntu1404安装xen
- 云计算的三种服务模式:IaaS,PaaS和SaaS
- mysql主从复制配置
- 7.4.2 Prime Ring Problem UVA 524
- HTML语言marquee实现滚动效果
- C++设计模式浅识桥接模式
- 服务器中 配置phpstudy一键安装包
- 6.4.1 用DFS求连通块 UVA 572 Oil Deposits DFS水题
- PAT(乙级) 1006. 换个格式输出整数
- ImageLoader blur
- 关于前端我的第一篇博客
- Android四大组件之BroadcastReceiver
- codeforces round 17 D(扩展欧拉函数的应用)
- mysql 分区
- Failed to run the WC DB work queue associated with 错误的解决
- 1775 Sum of Factorials 全排列解法+规律解法
- Linux下使用fdisk扩大分区容量
- Hibernate——session(2)
- 在BrodcastReceiver和Service内启动Activit和弹出dialog需要注意的地方
- 编译php7 opcache问题