由于最近在一家数据服务公司实习,项目需要了解分布式,所以在这里基于
scrapy
的分布式总结一下爬虫的分布式实习
分布式起因 #
单机无法完成全部工作任务所以要使用集群加速完成工作任务
分布式有点像蚁群,一只蚂蚁举不起一只卡壳虫,但是几百只就能轻松的把他运回家
但是分布式设计必须科学,否则就像下面一样,一个和尚挑水,其他和尚围观
分布式设计 #
分布式设计原理在于分工
首先我们来看看爬虫怎么进行分工,单个爬虫运行根据url
获取响应报文,然后通过解析报文返回结果或者下一次爬取目标,如果单个爬虫我们只要在内存维持一个set
变量记住爬取过的url
,这就是scrapy
默认的方法。
但是我们无数个爬虫由于不在同一个进程,无法共享变量,所以我们只要让一个“variable
(变量)”能够被被所以爬虫共享到就完成了主要功能
现在我们来完善具体细节
要求:
- 爬虫能够轻松读取所以已爬取变量
- 爬虫能够加入已读取变量
- 爬虫能够获取下一次请求具体参数
原则上我们可以使用内存映射来构建这个变量,但是读取,修改都不便利,所以可以先使用redis
作为存贮变量的地方,使用redis
提供的set
我们替代scrapy
框架的set
变量。
现在我们已经决定我们要使用什么容器来存贮变量,接下来我们要考虑存什么变量。
我们先看scrapy-redis
存贮了什么,分析源代码可知,scrapy-redis
将返回的Request
pickle话存入数据库,并且计算这个Request
的32位hash值存入redis
的set
中过滤列表。
scrapy-redis
通过修改scrapy
的调度器(scheduler)让其当爬虫没有Request
需要处理时在redis
中提取Request
,实现分布式。
我们来分析一下这种方法,爬虫在爬取的过程中从master
端获取Request
,并不断生成Request
到master
端,master
只是一个redis
数据库,负责对url
去重,分发任务。
我们来比较一下直接存取url
这种方法,这种方法好处在于,slaver
能够从上一个Request
中获取全部信息,假如上一个Request
需要存取获取的表单提取地址,我们下一次爬虫发起Request
就能从上一个Request
中获取参数。
当然由于我们存贮的是Request
,一个Request
pickle
化之后的字符串比较长,当我们的任务列表里面有很多Request
的时候,redis
占用的内存会非常巨大。
当然如果爬虫启动的够多,生成一个就能把任务被调度下去,那么这个任务列表就能稳定在一个可控的范围。
总结
每个爬虫即负责爬取数据,又负责生成下一个任务,即无主次之分,我们可以一次性在docker
中启动上百个实例,我们只是用redis
充当一个存放变量
的地方。
但是这种方法也有一个缺点,我们不能自由的添加初始url
,要想添加新的爬取任务,必须新建一个爬虫更新初始url
,我们如果是想搭建一个自由添加url
的爬虫,这种实现方式不大优雅。
分布式改良 #
我们要修改程序框架,达到随时可以添加要爬取新任务,然而不影响爬虫集群
我们独立出来master
,master
负责生成Request
去重以及任务调度,而slaver
只负责从master
获取任务爬取。
这种方法我们可以很轻松对master
改良而不影响slaver
,通过让master
定时从数据库
中获取新的任务生成到任务列表,我们可以轻松添加新的任务到slaver
集群中去。
下一步我们就介绍如何修改scrapy-redis
达到我们新框架需要