由于最近在一家数据服务公司实习,项目需要了解分布式,所以在这里基于
scrapy的分布式总结一下爬虫的分布式实习
分布式起因 #
单机无法完成全部工作任务所以要使用集群加速完成工作任务
分布式有点像蚁群,一只蚂蚁举不起一只卡壳虫,但是几百只就能轻松的把他运回家
但是分布式设计必须科学,否则就像下面一样,一个和尚挑水,其他和尚围观
分布式设计 #
分布式设计原理在于分工
首先我们来看看爬虫怎么进行分工,单个爬虫运行根据url获取响应报文,然后通过解析报文返回结果或者下一次爬取目标,如果单个爬虫我们只要在内存维持一个set变量记住爬取过的url,这就是scrapy默认的方法。
但是我们无数个爬虫由于不在同一个进程,无法共享变量,所以我们只要让一个“variable(变量)”能够被被所以爬虫共享到就完成了主要功能
现在我们来完善具体细节
要求:
- 爬虫能够轻松读取所以已爬取变量
- 爬虫能够加入已读取变量
- 爬虫能够获取下一次请求具体参数
原则上我们可以使用内存映射来构建这个变量,但是读取,修改都不便利,所以可以先使用redis作为存贮变量的地方,使用redis提供的set我们替代scrapy框架的set变量。
现在我们已经决定我们要使用什么容器来存贮变量,接下来我们要考虑存什么变量。
我们先看scrapy-redis存贮了什么,分析源代码可知,scrapy-redis将返回的Requestpickle话存入数据库,并且计算这个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达到我们新框架需要