Scrapy Redis

Scrapy Redis

Scrapy自带的待爬队列是deque,而现在需要使用Redis来作为队列,所以就需要将原来操作deque的方法替换为操作Redis的方法。当你把三轮车换成挖掘机的时候,驾驶员肯定是要更换的。Scrapy_redis在这里就是充当驾驶员的角色。更准确的说,Scrapy_redis是Scrapy的“组件”,它已经封装了使用Scrapy 操作Redis的各个方法。


安装

使用pip安装:

$ pip install scrapy_redis

在使用Scrapy_redis之前,先来讲一讲它的原理。

在Redis中,有一个数据结果叫做“列表”。这个列表和 Python 的列表很相似,可以添加数据进去,也可以从里面读取数据或者删除元素。

在下图的例子中,创建一个名字叫做 spider 的列表,从列表的尾部添加了三个元素:“hotstone”, "shilei" 和 "slzcc",然后再从列表的头部一个一个元素的读取并删除。

127.0.0.1:6379> lrange spider 0 -1
(empty list or set)
127.0.0.1:6379> rpush spider 'hotstone'
(integer) 1
127.0.0.1:6379> rpush spider 'shilei'
(integer) 2
127.0.0.1:6379> rpush spider 'slzcc'
(integer) 3
127.0.0.1:6379> lrange spider 0 -1
1) "hotstone"
2) "shilei"
3) "slzcc"
127.0.0.1:6379> lpop spider
"hotstone"
127.0.0.1:6379> lpop spider
"shilei"
127.0.0.1:6379> lpop spider
"slzcc"

在 Scrapy_redis 中,也是使用了这个原理,Scrapy 通过:

yield scrapy.Request(URL, callback=xxxx)

这一个语句将URL放到Redis的队列中,然后在生成器中的代码被执行的时候再去Redis中取这个URL。如果多个服务器的爬虫都从Redis中读取URL的话,这就是分布式爬虫。

Scrapy_redis 的使用

由于Scrapy_redis已经为封装了大部分的流程,所以使用它不会有任何难度。

修改爬虫

爬虫是继承自scrapy.Spider这个父类。这是Scrapy里面最基本的一个爬虫类,只能实现基本的爬虫功能。现在需要把它替换掉,从而实现更高级的功能。

请对比一下下面这段使用了Scrapy_redis的代码与前面read color网站爬虫的代码头部有什么不同:

from scrapy_redis.spiders import RedisSpider
 
class ReadColorSpider(RedisSpider):
    name = "zhihuspider"
    redis_key = 'zhihuspider:start_urls'

可以看出,这里爬虫的父类已经改成了RedisSpider,同时多了一个:

redis_key = 'zhihuspider:start_urls'

这里的redis_key实际上就是一个变量名,之后爬虫爬到的所有URL都会保存到Redis中这个名为“zhihuspider:start_urls”的列表下面,爬虫同时也会从这个列表中读取后续页面的URL。这个变量名可以任意修改。

除了这两点以外,在爬虫部分的其他代码都不需要做修改。

实际上,这样就已经建立了一个分布式爬虫,只不过现在只有一台电脑。

修改设置

现在已经把三轮车换成了挖掘机,但是Scrapy还在按照指挥三轮车的方式指挥挖掘机,所以挖掘机还不能正常工作。因此修改爬虫文件还不行,Scrapy还不能认识这个新的爬虫。现在修改settings.py。

(1)Scheduler

首先是Scheduler的替换,这个东西是Scrapy中的调度员。在settings.py中添加以下代码:

# Enables scheduling storing requests queue in redis.
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

(2)去重

# Ensure all spiders share same duplicates filter through redis.
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

设置好上面两项以后,爬虫已经可以正常开始工作了。不过还可以多设置一些东西使爬虫更好用。

(3)不清理Redis队列

# Don't cleanup redis queues, allows to pause/resume crawls.
SCHEDULER_PERSIST = True

如果这一项为True,那么在Redis中的URL不会被Scrapy_redis清理掉,这样的好处是:爬虫停止了再重新启动,它会从上次暂停的地方开始继续爬取。但是它的弊端也很明显,如果有多个爬虫都要从这里读取URL,需要另外写一段代码来防止重复爬取。

如果设置成了False,那么Scrapy_redis每一次读取了URL以后,就会把这个URL给删除。这样的好处是:多个服务器的爬虫不会拿到同一个URL,也就不会重复爬取。但弊端是:爬虫暂停以后再重新启动,它会重新开始爬。

爬虫请求的调度算法

爬虫的请求调度算法,有三种情况可供选择:

  • 队列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderQueue'

如果不配置调度算法,默认就会使用这种方式。它实现了一个先入先出的队列,先放进Redis的请求会优先爬取。

SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderStack'

这种方式,后放入到Redis的请求会优先爬取。

  • 优先级队列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue'

这种方式,会根据一个优先级算法来计算哪些请求先爬取,哪些请求后爬取。这个优先级算法比较复杂,会综合考虑请求的深度等各个因素。

【拓展阅读】

Redis信息,如果不配置的话,Scrapy_redis会默认Redis就运行在现在这台电脑上,IP和端口也都是默认的127.0.0.1和6379。如果Redis不在本地的话,就需要将它们写出来:

REDIS_HOST = '127.0.0.1' #修改为Redis的实际IP地址
REDIS_PORT = 6379 #修改为Redis的实际端口

代码展示:

爬虫的运行.zip

修复 BUG 

如果出现了 scrapy_redis 字符串报错的信息请修改,路径有可能不一样,这里使用 Macbook brew 安装后的路径:

/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/scrapy_redis/spiders.py

中添加,如图

data = data.decode()

配置文件讲解

#启用Redis调度存储请求队列SCHEDULER = "scrapy_redis.scheduler.Scheduler"
  
#确保所有的爬虫通过Redis去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
  
#默认请求序列化使用的是pickle 但是我们可以更改为其他类似的。PS:这玩意儿2.X的可以用。3.X的不能用
#SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"
  
#不清除Redis队列、这样可以暂停/恢复 爬取
#SCHEDULER_PERSIST = True
  
#使用优先级调度请求队列 (默认使用)
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
#可选用的其它队列
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
  
#最大空闲时间防止分布式爬虫因为等待而关闭
#SCHEDULER_IDLE_BEFORE_CLOSE = 10
  
#将清除的项目在redis进行处理
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline'300
}
  
#序列化项目管道作为redis Key存储
#REDIS_ITEMS_KEY = '%(spider)s:items'
  
#默认使用ScrapyJSONEncoder进行项目序列化
#You can use any importable path to a callable object.
#REDIS_ITEMS_SERIALIZER = 'json.dumps'
  
#指定连接到redis时使用的端口和地址(可选)
#REDIS_HOST = 'localhost'
#REDIS_PORT = 6379
  
#指定用于连接redis的URL(可选)
#如果设置此项,则此项优先级高于设置的REDIS_HOST 和 REDIS_PORT
#REDIS_URL = 'redis://user:pass@hostname:9001'
  
#自定义的redis参数(连接超时之类的)
#REDIS_PARAMS  = {}
  
#自定义redis客户端类
#REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'
  
#如果为True,则使用redis的'spop'进行操作。
#如果需要避免起始网址列表出现重复,这个选项非常有用。开启此选项urls必须通过sadd添加,否则会出现类型错误。
#REDIS_START_URLS_AS_SET = False
  
#RedisSpider和RedisCrawlSpider默认 start_usls 键
#REDIS_START_URLS_KEY = '%(name)s:start_urls'
  
#设置redis使用utf-8之外的编码
#REDIS_ENCODING = 'latin1'

官方地址介绍:http://scrapy-redis.readthedocs.io/en/stable/readme.html

相关文档:http://cuiqingcai.com/4048.html

原文地址:https://www.cnblogs.com/dalton/p/11353871.html