一 原理

scrapy调度系统

https://doc.scrapy.org/en/latest/topics/architecture.html#component-scheduler


分布式爬虫 scrapy-redis - 图1
scrapy-redis调度系统
分布式爬虫 scrapy-redis - 图2

  • scrapy默认是使用队列来管理请求的URL(存在内存中)

  • scrapy-redis是使用redis数据库来替代队列,用于URL的管理,去重等

二 安装

1 安装scrapy-reids和redis(CentOS7环境)

redis可视化客户端:https://legacy.gitbook.com/book/fategithub/pythonspider/edit#

  1. pip install scrapy-redis
  2. # 先安装remi软件源
  3. yum install -y http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
  4. # 使用remi源安装最新版的redis
  5. yum --enablerepo=remi install redis

2 redis配置和开启

配置文件 /etc/redis.conf

  1. bind 0.0.0.0 # 允许外网访问
  2. port 6379 # 默认端口 外网访问还需要配置防火墙
  3. requirepass xxx # 开启认证,默认不开启

开启服务

  1. ./redis-server

设置开机启动

  1. chkconfig redis on
  2. # or
  3. systemctl enable redis.service

三 示例

1 配置文件 settings.py

配置redis服务器 使用scrapy-redis的DUPEFILTER_CLASS和SCHEDULER代替scrapy自带的

  1. # *** Redis连接
  2. REDIS_HOST = "xxx.xxx.xxx.xxx"
  3. REDIS_PORT = "6379"
  4. REDIS_PARAMS = {'password': 'your_password'} # 可选
  5. # REDIS_ENCODING = 'utf-8'
  6. # *** 使用scrapy-redis组件的去重队列
  7. DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
  8. # *** 使用scrapy-redis组件的调度器
  9. SCHEDULER = "scrapy_redis.scheduler.Scheduler"
  10. # 是否允许暂停(即不清空dupefilter队列)
  11. SCHEDULER_PERSIST = True
  12. # 等待时间
  13. SCHEDULER_IDLE_BEFORE_CLOSE = 5
  14. # DUPEFILTER_DEBUG = True

如果想把爬取的数据存到redis数据库中,可使用scrapy-redis的scrapy_redis.pipelines.RedisPipeline组件

  1. ITEM_PIPELINES = {
  2. 'lovd.pipelines.LovdPipeline': 300,
  3. 'scrapy_redis.pipelines.RedisPipeline': 301, # 数值越小,优先级越高
  4. }

2 编写爬虫

scrapy的Spider,例如:

class LovdSpiderSpider(scrapy.Spider):
    name = 'lovd_spider'
    allowed_domains = ['databases.lovd.nl']

替换为scrapy-redis的Spider

方式1:开启爬虫后监听redis_key

from scrapy_redis.spiders import RedisCrawlSpider

class LovdSpiderSpider(RedisCrawlSpider):
    name = 'lovd_variants_spider'
    redis_key = "lovd_variants_spider:start_urls"  # 触发条件, 默认为'spider_name:start_urls'

    def parse(self, response):
        pass

开启爬虫

scrapy crawl lovd_variants_spider

添加redis_key,爬虫会自动监听redis中key为redis_key的列表,并开始爬取第一个页面

lpush lovd_variants_spider:start_urls https://databases.lovd.nl/shared/variants/in_gene

方式2:重构start_requests函数,自动开始请求

数据可通过meta来传输

base_url = 'https://databases.lovd.nl/shared/variants/in_gene#'

def start_requests(self):
    params = {'page': 1}
    yield scrapy.Request(self.base_url, meta={'params': params})

def parse(self, response):
    if response.meta['params']['page'] < 10:  # 结束条件
        response.meta['params']['page'] += 1  # 下一页
        url = self.base_url + str(response.meta['params']['page'])
        response.follow(url, meta=response.meta)

PS: 如果要输出的数据需要通过第一个页面跳转,第一个页面可以不用过滤,dont_filter=True

这种方式启动爬虫后会自动开始爬取

3 分布式

  • 所谓分布式,就是可以把爬虫放到不同的地方启动,多个爬虫共用一个redis,获取要爬取的URL
  • 已爬取的URL会转成hash后,存到redis中的一个set中(redis_key:dupefilter),从而实现去重的效果

把你的项目拷贝到不同的主机上,开启爬虫即可

4 结果数据

不同主机上的爬取结果,如果没有配置ITEM_PIPELINES,只能通过-o的方式输出到各自的主机上
如果想统一输出的一个地方,可以:

  • 使用scrapy-redis提供的RedisPipeline,这样每个爬虫都会把结果输出到redis的列表(scrapy_key:items)中,不过这种方式不适合爬取较大的数据(redis是运行在内存的)
  • 自己配置Pipeline,如使用MongoDB进行配置

四 问题

scrapy-reids爬虫不能自动停止?

机制就是爬虫监听redis_key,获取到URL就爬 https://stackoverflow.com/questions/45540569/the-scrapy-redis-program-does-not-close-automatically

大数据量下的过滤解决方案?

可以考虑使用bloom filter或者ssdb来进行过滤