python分布式爬虫框架 --- scrapy-redis

scrapy-redis模块

scrapy-redis是为了实现scrapy的分布式爬取而提供了一个python库,通过更换scrapy的内置组件,将爬取请求队列和item数据放入第三方的redis数据库中,由此可以有多个scrapy进程从redis中读取request数据和写入items数据,实现分布式处理。

redis主要工作:

  • 储存request请求,形成一个队列供不同的多个scrapy进行消费。scrapy-redis提供了三种队列,可自由配置。
    • PriorityQueue(优先队列:默认):通过redis的有序集合实现,同时集合可以自动对request去重
    • FifoQueue和LifoQueue:通过List实现
  • 储存item数据
  • 对request进行去重,需要使用一个集合的数据结构来实现快速的去重。

安装scrapy-redis

pip install scrapy-redis即可安装,官方要求python和redis版本如下:

  • Python 2.7, 3.4 or 3.5
  • Redis >= 2.8
  • Scrapy >= 1.1
  • redis-py >= 2.10

配置scrapy-redis

scrapy-redis是在原scrapy的基础上,通过替换部分scrapy组件来实现,这些组件包括。

  • Scheduler:使用redis作为request的调度队列,需要更换schedule类,配置文件中配置即可
  • Duplication Filter:通过redis的set对request进行去重,去重的指纹采用method+url+body+header的信息,通过hash函数生成hash值存入redis的一个set中,使用时在配置文件中直接配置即可
  • Item Pipeline:使用一个pipeline将爬取的数据储存到redis服务中,配置文件中添加该类即可
  • Base Spiders:spider对象是一个爬虫的核心对象,该类的定义由我们自己实现,在原来scarapy框架中通过继承内置Spider类来获得了公用的功能。这里我们需要使用scrapy-redis提供的RedisSpider类作为父类,继承与redis进行连接通信的方法,而不用我们手动连接redis。

更换上述的组件的方式通过配置文件即可完成,只有spider类的定义需要我们更改继承的父类即可。

配置文件

作者给出可添加的配置文件的全部内容,需要时进行添加配置即可。githu地址https://github.com/rmax/scrapy-redis

# Enables scheduling storing requests queue in redis.
配置后使用redis中队列实现了request的调度。
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

# 过滤重复request方式,避免重复爬取
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

# 默认使用pickle进行序列化,可以指定为“json”或 “msgpack”
#SCHEDULER_SERIALIZER = "json"  # "msgpack"   json和msgpack均可调用dump和loads方法
SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"

# 不会清空request队列,阻塞等待downloader获取request爬取
#SCHEDULER_PERSIST = True

# Schedule requests 默认使用PriorityQueue (优先队列)
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
# 其他可选的有
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'

# Max idle(闲置) time 分布式情况下最大的闲置时间,超过该时间该spider没有获取数据则可能已经关闭.
# queue class 是 SpiderQueue or SpiderStack 时才生效
# 这可能会让你的爬虫刚开始启动时阻塞的,因为这个queue是空的
#SCHEDULER_IDLE_BEFORE_CLOSE = 10


# Store scraped item in redis for post-processing.
# 处理item的类,该类会将item保存到redis的中,并使用指定的序列化方式,和REDIS_ITEMS_KEY指定的key储存
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300
}

# item信息被序列化后储存在该key中,默认key为
REDIS_ITEMS_KEY = '%(spider)s:items'

# 将item对象进行序列化的方式,默认为 ScrapyJSONEncoder. 可以指定其他序列化方式,指定一个序列化函数即可,例如下面json.dump
#REDIS_ITEMS_SERIALIZER = 'json.dumps'

# Specify the host and port to use when connecting to Redis (optional).
#REDIS_HOST = 'localhost'
#REDIS_PORT = 6379
# 设置后优先选择url的方式连接
#REDIS_URL = 'redis://user:pass@hostname:9001'

# Custom redis client parameters (i.e.: socket timeout, etc.)
#REDIS_PARAMS  = {}
# Use custom redis client class.
#REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'

# 多个start_url时是否储存为一个set,方便去重
#REDIS_START_URLS_AS_SET = False

# starturl储存在redis中的key的名字
#REDIS_START_URLS_KEY = '%(name)s:start_urls'

# Use other encoding than utf-8 for redis.
#REDIS_ENCODING = 'latin1'

以上是全部的配置文件的内容,通常不用进行全部的配置,添加下面的配置即可

# 更换scheduler组件
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'   # 如果不指定使用优先队列

# 指定对request去重所使用的类,该类继承自scrapy中的饿BaseDupeFilter,重写了部分方法
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

# 更改原配置文件中的ITEM_PIPELINES
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300,  # 该pipeline是item数据写入redis,也可以指定其他pipeline
}

# 在redis中保存item数据的key的名字
REDIS_ITEMS_KEY = '%(spider)s:items'
REDIS_HOST = '192.168.236.100'
REDIS_PORT = 6379

# start_url储存在redis中的key的名字, 该值在会绑定在spider对象的redis_key属性上
REDIS_START_URLS_KEY = '%(name)s:start_urls'

spider

spider需要从scrapy-redis模块中继承spider类,scrapy中有两个spider类,分别为Spider和CrawlSpider类,scrapy-redis通过Mixin的方式,将RedisMixin类混入原来的两个spider类中,得到了RedisSpider和RedisCrawlSpider,使用时将原来的spider类对应更换为新的RedisSpider或RedisCrawlSpider类即可

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import Rule  # CrawlSpider
from scrapy.http.response.html import HtmlResponse
from scrapy_redis.spiders import RedisCrawlSpider


class ReviewSpider(RedisCrawlSpider):  # 使用RedisCrawlSpider替换原CrawlSpider类
    name = 'name'
    allowed_domains = ['domain.com']
    # 如果指定了start_urls,会将该start_urls存如redis对应的指定的key中
    # 如果没有指定,scrapy将阻塞等待redis的start_urls对应的key中被添加url来开始爬取
    # start_urls = []
    redis_key = "reviews:start_url"   # start_url对应redis中的key,没有指定使用配置文件中的REDIS_START_URLS_KEY = '%(name)s:start_urls'配置的值作为key

    rules = (
        Rule(LinkExtractor(allow=r'Item'), callback='parse_item', follow=True),
    )

    def parse_item(self, response: HtmlResponse):
        pass
        # return items

运行scrapy

更改以上配置后,运行该scrapy爬虫,由于start_url中没有起始的url,程序将会阻塞等待提供起始url,scrapy时从redis中获取url,且key为上述spider中定义的redis_key属性的内容,即"reviews:start_url",所以只需要手动向该key写入一个start_url数据即可开始爬取。该key的类型默认为一个List,(除非配置文件中有一个选项指定该key的为一个set)。

进入redis客户端,执行lpush reviews:start_url <start_url>添加一个初始url即可开始爬取。

运行后查看redis中的数据,增加了三个key信息。

  • review:dupefilter:类型为set,存放已爬取request指纹信息,保存的是每个request经过hash函数计算后的结果
  • review:items:类型为list, 存放经过序列化后的item数据,默认使用json.dumps的序列化方式。
  • review;start_url:类型为list,起始的url数据。

 

原文地址:https://www.cnblogs.com/k5210202/p/13079652.html