前言
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'