• scrapy-redis的使用和解析


    scrapy-redis是一个基于redis的scrapy组件,通过它可以快速实现简单分布式爬虫程序,该组件本质上提供了三大功能:

    • scheduler - 调度器  
    • dupefilter - URL去重规则(被调度器使用)
    • pipeline   - 数据持久化    (详细信息

    基于scrapy-redis的去重规则

    完全自定义 
    					from scrapy.dupefilter import BaseDupeFilter
    					import redis
    					from scrapy.utils.request import request_fingerprint
    
    					class DupFilter(BaseDupeFilter):
    						def __init__(self):
    							self.conn = redis.Redis(host='140.143.227.206',port=8888,password='beta')
    
    						def request_seen(self, request):
    							"""
    							检测当前请求是否已经被访问过
    							:param request: 
    							:return: True表示已经访问过;False表示未访问过
    							"""
    							fid = request_fingerprint(request)
    							result = self.conn.sadd('visited_urls', fid)
    							if result == 1:
    								return False
    							return True
    

     

    继承scrapy-redis实现自定制 ;就是把去重规则放入redis中,利用redis中的集合

    from scrapy_redis.dupefilter import RFPDupeFilter
    					from scrapy_redis.connection import get_redis_from_settings
    					from scrapy_redis import defaults
    
    					class RedisDupeFilter(RFPDupeFilter):
    						@classmethod
    						def from_settings(cls, settings):
    							"""Returns an instance from given settings.
    
    							This uses by default the key ``dupefilter:<timestamp>``. When using the
    							``scrapy_redis.scheduler.Scheduler`` class, this method is not used as
    							it needs to pass the spider name in the key.
    
    							Parameters
    							----------
    							settings : scrapy.settings.Settings
    
    							Returns
    							-------
    							RFPDupeFilter
    								A RFPDupeFilter instance.
    
    
    							"""
    							server = get_redis_from_settings(settings)
    							# XXX: This creates one-time key. needed to support to use this
    							# class as standalone dupefilter with scrapy's default scheduler
    							# if scrapy passes spider on open() method this wouldn't be needed
    							# TODO: Use SCRAPY_JOB env as default and fallback to timestamp.
    							key = defaults.DUPEFILTER_KEY % {'timestamp': 'xiaodongbei'}
    							debug = settings.getbool('DUPEFILTER_DEBUG')
    							return cls(server, key=key, debug=debug)
    
    - 配置:
    				
    				# ############### scrapy redis连接 ####################
    
    				REDIS_HOST = '127.0.0.1'                            # 主机名
    				REDIS_PORT = 8888                                   # 端口
    				REDIS_PARAMS  = {'password':'beta'}                                  # Redis连接参数           
    				REDIS_ENCODING = "utf-8"                            # redis编码类型             默认:'utf-8'
    				# REDIS_URL = 'redis://user:pass@hostname:9001'       # 连接URL(优先于以上配置)
    				
    				# ############### scrapy redis去重 ####################
    				
    				
    				DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
    				
    				# DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
    				DUPEFILTER_CLASS = 'dbd.xxx.RedisDupeFilter'
    

      

    scrapy-redis 源码内部提供了三种队列
    - 先进先出
    - 后进先出
    - 优先级队列

      

    scrapy-redis组件的执行流程

    1. scrapy crawl chouti --nolog
    	
    2. 找到 SCHEDULER = "scrapy_redis.scheduler.Scheduler" 配置并实例化调度器对象
    	- 执行Scheduler.from_crawler
    	- 执行Scheduler.from_settings
    		- 读取配置文件:
    			SCHEDULER_PERSIST			 # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
    			SCHEDULER_FLUSH_ON_START     # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
    			SCHEDULER_IDLE_BEFORE_CLOSE  # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。
    		- 读取配置文件:	
    			SCHEDULER_QUEUE_KEY			 # %(spider)s:requests
    			SCHEDULER_QUEUE_CLASS		 # scrapy_redis.queue.FifoQueue
    			SCHEDULER_DUPEFILTER_KEY     # '%(spider)s:dupefilter'
    			DUPEFILTER_CLASS			 # 'scrapy_redis.dupefilter.RFPDupeFilter'
    			SCHEDULER_SERIALIZER		 # "scrapy_redis.picklecompat"
    
    		- 读取配置文件:
    			REDIS_HOST = '140.143.227.206'                            # 主机名
    			REDIS_PORT = 8888                                   # 端口
    			REDIS_PARAMS  = {'password':'beta'}                                  # Redis连接参数             默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
    			REDIS_ENCODING = "utf-8"      
    	- 实例化Scheduler对象
    	
    3. 爬虫开始执行起始URL
    	- 调用 scheduler.enqueue_requests()
    		def enqueue_request(self, request):
    			# 请求是否需要过滤?
    			# 去重规则中是否已经有?(是否已经访问过,如果未访问添加到去重记录中。)
    			if not request.dont_filter and self.df.request_seen(request):
    				self.df.log(request, self.spider)
    				# 已经访问过就不要再访问了
    				return False
    			
    			if self.stats:
    				self.stats.inc_value('scheduler/enqueued/redis', spider=self.spider)
    			# print('未访问过,添加到调度器', request)
    			self.queue.push(request)
    			return True
    	
    4. 下载器去调度器中获取任务,去下载
    	
    	- 调用 scheduler.next_requests()
    		def next_request(self):
    			block_pop_timeout = self.idle_before_close
    			request = self.queue.pop(block_pop_timeout)
    			if request and self.stats:
    				self.stats.inc_value('scheduler/dequeued/redis', spider=self.spider)
    			return request
    

    什么是深度优先?什么是广度优先?
    深度:一直爬到底,然后在爬其他的;广度:爬完第一层,在爬取第二层。

    scrapy中如何实现深度和广度优先?
    先进先出,广度优先 (趋势)
    后进先出,深度优先;

    优先级队列:()
    DEPTH_PRIORITY = 1 # 广度优先
    DEPTH_PRIORITY = -1 # 深度优先

    scrapy中 调度器 和 队列 和 dupefilter的关系?
    调度器,调配添加或获取(pop)那个request.
    队列,存放request。
    dupefilter,访问记录。有就不存入队列;没有就存入队列中。

    配置
    连接redis配置:
    REDIS_HOST = '140.143.227.206' # 主机名
    REDIS_PORT = 8888 # 端口
    REDIS_PARAMS = {'password':'beta'} # Redis连接参数 默认:REDIS_PARAMS = {'socket_timeout': 30,'socket_connect_timeout': 30,'retry_on_timeout': True,'encoding': REDIS_ENCODING,})
    REDIS_ENCODING = "utf-8" # redis编码类型 默认:'utf-8'

    去重的配置:
    DUPEFILTER_KEY = 'dupefilter:%(timestamp)s'
    DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'

    调度器配置:
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"

    DEPTH_PRIORITY = 1 # 广度优先
    # DEPTH_PRIORITY = -1 # 深度优先
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue' # 默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)

    # 广度优先
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue' # 默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
    # 深度优先
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue' # 默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
    SCHEDULER_QUEUE_KEY = '%(spider)s:requests' # 调度器中请求存放在redis中的key

    SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat" # 对保存到redis中的数据进行序列化,默认使用pickle

    SCHEDULER_PERSIST = False # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
    SCHEDULER_FLUSH_ON_START = True # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
    # SCHEDULER_IDLE_BEFORE_CLOSE = 10 # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。


    SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter' # 去重规则,在redis中保存时对应的key

    # 优先使用DUPEFILTER_CLASS,如果么有就是用SCHEDULER_DUPEFILTER_CLASS
    SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter' # 去重规则对应处理的类

     

  • 相关阅读:
    java实现操作系统磁盘寻道先来先服务算法
    专业素养:发布文件,别忘了给出校验信息
    vue系列教程-08vue的动画和过渡效果
    vue系列教程-07vue动态绑定样式
    vue系列教程-06vue的事件处理
    vue系列教程-05vue常用指令
    vue系列教程-04vue数据处理和页面渲染
    vue系列教程-03vuejs的结构和生命周期
    vue系列教程-01第一个vue程序
    vue系列教程-02什么是mvvm和spa
  • 原文地址:https://www.cnblogs.com/zenghui-python/p/11654907.html
Copyright © 2020-2023  润新知