scrapy_redis解析与实战应用

前言

scrapy-redis是一个基于redis的scrapy组件,用于快速实现scrapy项目的分布式部署和数据爬取,其运行原理如下图所示。
在这里插入图片描述

一、Scrapy-Redis特性

分布式爬取

你可以启动多个共享同一redis队列的爬虫实例,多个爬虫实例将各自提取到或者已请求的Requests在队列中统一进行登记,使得Scheduler在请求调度时能够对重复Requests进行过滤,即保证已经由某一个爬虫实例请求过的Request将不会再被其他的爬虫实例重复请求。

分布式数据处理

将scrapy爬取到的items汇聚到同一个redis队列中,意味着你可以根据你的需要启动尽可能多的共享这个items队列的后处理程序。

Scrapy即插即用组件

Scheduler调度器 + Duplication重复过滤器、Item Pipeline、基础Spider爬虫


二、基于scrapy做了哪些变化

1.添加scrapy_redis.dupefilter.RFPDupeFilter

scrapy_redis.dupefilter.RFPDupeFilter是基于redis就行去重,从而实现分布式,而scrapy.dupefilter.RFPDupeFilter是基于内存set进行去重。这个类可以自己定制,如果数据量过大可以实现利用布隆过滤器进行去重。

2.添加scrapy_redis.stats.RedisStatsCollector

scrapy_redis.stats.RedisStatsCollector是利用redis就行保存实时统计的数据,从而实现分布式,而scrapy.statscollectors.MemoryStatsCollector是利用内存,python字典就行保存数据

3.添加scrapy_reids.scheduler.Scheduler

scrapy_reids新添加了基于redis的队列
SpiderQueue = FifoQueue 先进先出,基于redis
SpiderStack = LifoQueue 后进先出,基于redis
SpiderPriorityQueue = PriorityQueue 优先级队列,基于redis
在scrapy_reids.scheduler.Scheduler中利用以上队列实现分布式任务添加和获取,从而实现分布式部署。

4.添加scrapy_redis.pipeline.RedisPipeline

scrapy中没有实现默认的pipeline类,而scrapy_redis中添加scrapy_redis.pipeline.RedisPipeline,汇总分布式集群采集的数据,有利于后续数据处理。

5.添加RedisSpider、RedisCrawlSpider、RedisMixin

可以通过继承RedisSpider、RedisCrawlSpider、RedisMixin,实现自己的Spider从redis中获取start_urls
实战案例

import json
from scrapy import Spider, signals, FormRequest
from scrapy_redis.spiders import RedisSpider
from scrapy_redis.utils import bytes_to_str


class IfengSpider(RedisSpider):
    name = 'ifeng'
    allowed_domains = ['ifeng.com']

    # start_urls = ['https://ifeng.com/']

    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        spider = super(IfengSpider, cls).from_crawler(crawler, *args, **kwargs)
        spider._set_crawler(crawler)
        crawler.signals.connect(spider.open_spider, signal=signals.spider_opened)
        crawler.signals.connect(spider.close_spider, signal=signals.spider_closed)
        return spider

    def make_request_from_data(self, data):
        '''Returns a Request instance from data coming from Redis.'''
        formatted_data = bytes_to_str(data, self.redis_encoding)

        # change to json array
        parameter = json.loads(formatted_data)
        url = parameter['url']
        del parameter['url']
        metadata = {}
        try:
            metadata = parameter['meta']
            del parameter['meta']
        except Exception:
            pass
        return FormRequest(url, dont_filter=True, formdata=parameter, meta=metadata)
        pass

    def parse(self, response, **kwargs):
        # print(response.text)
        # urls = response.xpath('//ul[@id="nav"]/li/a/@href').extract()
        import json
        print(json.dumps(response.meta))
        requests = response.follow_all(xpath='//ul[@id="nav"]/li/a/@href')
        print(list(requests))
        for row in response.xpath('//ul[@id="nav"]/li/a/@href').extract():
            yield {
                "url": row,
            }
        pass

    def closed(self, reason):
        self.logger.info('closed {}'.format(reason))

    def open_spider(self, spider: Spider):
        self.logger.info(f"-----------{self.__class__.__name__}-open_spider-----------")

    def close_spider(self, spider: Spider):
        self.logger.info(f"-----------{self.__class__.__name__}-close_spider-----------")

配置文件 settings

ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300
}

SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
STATS_CLASS = "scrapy_redis.stats.RedisStatsCollector"

REDIS_URL = 'redis://192.168.10.102:6379'
REDIS_HOST = '192.168.10.102'
REDIS_PORT = 6379

REDIS_START_URLS_AS_SET = True

redis中添加任务

sadd ifeng:start_urls '{"url":"https://ifeng.com/"}'

其它的代码和scrapy项目一样。

三、scrapy_redis新添加settings介绍

# For standalone use.
DUPEFILTER_KEY = 'dupefilter:%(timestamp)s' # 指定filter去重的redis key

PIPELINE_KEY = '%(spider)s:items'  # 指定pipeline存放item数据的redis key

STATS_KEY = '%(spider)s:stats' # 执行存放stats的redis key

REDIS_CLS = redis.StrictRedis
REDIS_ENCODING = 'utf-8'
# Sane connection defaults.
REDIS_PARAMS = {
    'socket_timeout': 30,
    'socket_connect_timeout': 30,
    'retry_on_timeout': True,
    'encoding': REDIS_ENCODING,
}

SCHEDULER_QUEUE_KEY = '%(spider)s:requests'  # 任务队列的key
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue' # 指定使用的优先级队列 srapy中有该参数
SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter' # 指定filter key
SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter' # 指定dupefiter类 srapy中有该参数
SCHEDULER_PERSIST = False 
START_URLS_KEY = '%(name)s:start_urls' # 存放start_urls 的key
START_URLS_AS_SET = False  # 指定url存放方式
START_URLS_AS_ZSET = False # 指定url存放方式
MAX_IDLE_TIME = 0

# 配置redis连接信息
REDIS_URL = ''
REDIS_HOST = ''
REDIS_PORT = ''
REDIS_DB = ''
REDIS_ENCODING= 'encoding'
REDIS_DECODE_RESPONSES = 'decode_responses'
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值