首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Scrapy-Redis分布式爬虫源码解析

Scrapy-Redis库已经为我们提供了Scrapy分布式的队列、调度器、去重等功能,其GitHub地址为:https://github.com/rmax/scrapy-redis。

本节我们深入了解一下,利用Redis如何实现Scrapy分布式。

1. 获取源码

可以把源码Clone下来,执行如下命令:

核心源码在scrapy-redis/src/scrapy_redis目录下。

2. 爬取队列

从爬取队列入手,看看它的具体实现。源码文件为queue.py,它有三个队列的实现,首先它实现了一个父类,提供一些基本方法和属性,如下所示:

首先看一下方法。我们要把一个Request对象存储到数据库中,但数据库无法直接存储对象,所以先要将Request序列化转成字符串,而这两个方法分别可以实现序列化和反序列化的操作,这个过程可以利用pickle库来实现。队列Queue在调用方法将Request存入数据库时,会调用方法进行序列化,在调用取出Request时,会调用进行反序列化。

在父类中,、和这三个方法都是未实现的,三个方法直接抛出异常,因此这个类不能直接使用。那么,必须要实现一个子类来重写这三个方法,而不同的子类就会有不同的实现和不同的功能。

接下来我们定义一些子类来继承类,并重写这几个方法。在源码中有三个子类的实现,它们分别是、、,我们分别来看看它们的实现原理。

首先是,如下所示:

这个类继承了类,并重写了、、三个方法,这三个方法都是对对象的操作。对象就是一个Redis连接对象,我们可以直接调用其操作Redis的方法对数据库进行操作,这里的操作方法有、、等,这就代表此爬取队列使用了Redis的列表。序列化后的Request会存入列表中,方法获取列表的长度,方法调用了操作,这代表从列表左侧存入数据,方法中调用了操作,这代表从列表右侧取出数据。

Request在列表中的存取顺序是左侧进、右侧出,这是有序的进出,即先进先出(First Input First Output,FIFO),此类的名称就叫作。

还有一个与之相反的实现类,叫作,实现如下:

与不同的是的方法,它使用的是操作,也就是从左侧出,方法依然使用操作,从左侧入。那么效果就是先进后出、后进先出(Last In First Out,LIFO),此类名称就叫作。这个存取方式类似栈的操作,所以也可以称作。

在源码中还有一个子类叫作,顾名思义,它是优先级队列,实现如下:

在这里、、方法使用了对象的、、操作,这里使用的存储结果是有序集合,这个集合中的每个元素都可以设置一个分数,这个分数就代表优先级。

方法调用了操作,返回的就是有序集合的大小,也就是爬取队列的长度。方法调用了操作,就是向集合中添加元素,这里的分数指定成Request的优先级的相反数,分数低的会排在集合的前面,即高优先级的Request就会在集合的最前面。方法首先调用了操作,取出集合的第一个元素,第一个元素就是最高优先级的Request,然后再调用操作,将这个元素删除,这样就完成了取出并删除的操作。

此队列是默认使用的队列,即爬取队列默认是使用有序集合来存储的。

3. 去重过滤

前面说过Scrapy的去重是利用集合来实现的,而在Scrapy分布式中的去重就需要利用共享的集合,那么这里使用的就是Redis中的集合数据结构。我们来看看去重类是怎样实现的,源码文件是dupefilter.py,其内实现了一个类,如下所示:

这里同样实现了一个方法,和Scrapy中的方法实现极其类似。不过这里集合使用的是对象的操作,也就是集合不再是一个简单数据结构了,而是直接换成了数据库的存储方式。

鉴别重复的方式还是使用指纹,指纹同样是依靠方法来获取的。获取指纹之后就直接向集合添加指纹,如果添加成功,说明这个指纹原本不存在于集合中,返回值1。代码中最后的返回结果是判定添加结果是否为0,如果刚才的返回值为1,那这个判定结果就是,也就是不重复,否则判定为重复。

这样我们就成功利用Redis的集合完成了指纹的记录和重复的验证。

4. 调度器

Scrapy-Redis还帮我们实现了配合Queue、DupeFilter使用的调度器Scheduler,源文件名称是scheduler.py。我们可以指定一些配置,如即是否在爬取开始的时候清空爬取队列,即是否在爬取结束后保持爬取队列不清除。我们可以在settings.py里自由配置,而此调度器很好地实现了对接。

接下来我们看看两个核心的存取方法,实现如下所示:

可以向队列中添加Request,核心操作就是调用Queue的操作,还有一些统计和日志操作。就是从队列中取Request,核心操作就是调用Queue的操作,此时如果队列中还有Request,则Request会直接取出来,爬取继续,否则如果队列为空,爬取则会重新开始。

5. 总结

目前为止,我们就之前所说的三个分布式的问题解决了,总结如下。

爬取队列的实现。这里提供了三种队列,使用了Redis的列表或有序集合来维护。

去重的实现。这里使用了Redis的集合来保存Request的指纹,以提供重复过滤。

中断后重新爬取的实现。中断后Redis的队列没有清空,爬取再次启动时,调度器的会从队列中取到下一个Request,爬取继续。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180308G1M3NZ00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券