43

scrapy和scrapy-redis的区别

 4 years ago
source link: https://www.tuicool.com/articles/2MJvYzz
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

scrapy是一个python爬虫框架,爬取的效率极高,具有高度的定制性,但是不支持分布式。而scrapy-redis是一套基于redis库,运行在scrapy框架之上的组件,可以让scapy支持分布式策略

Slaver端共享Master端redis数据库里的item 队列、请求队列和请求指纹集合。

选择redis数据库的原因:

redis支持主从同步,而且数据都是缓存在内存中的,所以基于redis的分布式爬虫,对请求和数据的高频率读取效率都非常高

scrapy-redis和scrapy的关系就像电脑和固态硬盘一样,是电脑中的一个插件,能让电脑更快的运行

scrapy是一个爬虫框架,scrapy-redis则是这个框架上可以选择的插件,它可以让爬虫跑得更 AfAbMzy.png!web

解释说明:

  1. 从优先级队列中获取requests对象,交给engine
  2. engine将requests对此昂交给下载器下载,期间会通过downlomiddleware的process_request方法
  3. 下载器完成下载,获得response对象,将该对象交给engine,期间会经过downloadmiddleware的process_response()方法
  4. engine将获得的response对象交给spider进行解析,期间会经过spidermiddleware的process_spider_input()方法
  5. spider解析下载器下载下来的response,返回item或links(url)
  6. item或者link经过spidermiddleware的process_spider_out()方法,交给engine
  7. engine将item交给item pipeline,将links交给调度器
  8. 在调度器中,先将requests对象利用scrapy内置的指纹函数生成一个指纹
  9. 如果requests对象中的don't filter参数设置为False,并且该requests对象的指纹不在信息指纹的队列中,那么就把该requests对象放到优先级队列中

中间件:

spider与engine之间(爬虫中间件)

介于scrapy引擎和爬虫之间的框架,主要工作就是处理爬虫的响应输入和请求的输出

download与engine之间(下载器中间件)

介于scrapy引擎和下载器之间的框架,主要是处理scrapy引擎与下载器之间的请求和响应

scrapy框架中的middleware.py

  1. Scrapy Middleware有以下几个函数被管理
    1. process_spider_input:接收一个response对象并处理
    2. process_spider_exception:spider出现异常时被调用
    3. process_spider_output:当spider处理response返回result时,就会调用该方法
    4. process_spider_requests:当spider发出请求时,被调用
  2. Download Middleware有以下几个函数被管理
    1. process_requests:requests通过下载中间件的时候,该方法被调用,这里可以通过设置代理,设置request.meta['proxy']就OK了
    2. process_response:下载结果经过中间件的时候会被这个方法解惑来进行处理
    3. process_exception:下载过程中出现异常的时候会被调用

Scrapy的优缺点:

  1. 优点
    1. scrapy是异步处理的,写middleware,方便写一些统一的过滤器
  2. 缺点
    1. 基于python的爬虫框架,扩展性比较差,基于twisted框架,运行中的exception是不会干掉reactor,并且异步框架出错后是不会停掉其他任务的,数据出错后难以察觉

Scrapy-Redis提供了四种组件(四种组件也就意味着这四个模块都需要做出相应的改动):

  1. Scheduler
    1. scrapy改变了python原本的collection.deque(双向队列)形成了自己的Scrapy queue,但是Scrapy多个spider不能共享爬取队列Scrapy queue,也就是Scrapy本身不支持爬虫的分布式,scrapy-redis的解决是把这个Scrapy queue换成了redis数据库(也是指redis队列),从同一个redis-server存放要爬取的request,就可以让多个爬虫去同一个数据库里读取了
    2. scrapy中跟待爬队列直接相关的就是调度器Scheduler,它负责对新的request进行入列操作(加入到Scrapy queue),取出下一个要爬的request(从Scrapy queue中取出来)等操作。它把待爬队列按照优先级建立一个字典结构,然后根据request中的优先级,再来决定该入哪个队列,出列时就按照优先级较小的优先出列,为了管理这个比较高级的队列字典,Scheduler需要提供一系列的方法,但是原来的Scheduler已经无法使用,所以使用Scrapy-redis的Scheduler组件
  2. Duplication Filter
    1. scrapy中用集合实现这个request的去重功能,scrapy中把已经发送的request指纹放入到一个集合中,把下一个request的指纹拿到集合中进行比较,如果该指纹已经存在集合中了,说了这个request发送过了,如果没有的话就继续这个操作
    2. 在scrapy-redis中去重是由Duplication Filter组件来实现的,它通过redis的set不重复的特性 ,巧妙的实现了Duplicating Filter去重。scrapy-redis调度器从引擎接受request,将request的指纹存入redis的set检查是否产生了重复,并将不重复的request push写入redis的request queue
    3. 引擎请求request时,调度器从redis的request queue队列里根据优先级进行pop出一个request返回给engine,engine将这个request发送给spider进行处理
  3. Item Pipeline
    1. 引擎(spider返回的)将爬取到的item给item pipeline,scrapy-redis的item pipeline将爬取到的item存入到redis的items queue
    2. 修改过的item pipeline可以很方便的根据key从items queue提取item,从而实现items processes集群
  4. Base Spider
    1. 不在使用scrapy原有的Spider类,重写RedisSpider继承了Spider和RedisMixin这两个类,RedisMixin是用来从redis中读取url的类
    2. 当我们生成一个Spider继承RedisSpider的时候,调用setup_redis函数,这个函数会去连接redis数据库,然后就设置signals(信号):一个是当spider空闲时候的signal,会调用spider_idle函数,这个函数调用scheduler_next_request函数,保证spider是一直活着的状态,并且抛出DontCloseSpider异常,还有一个就是当抓到一个item的signal,会调用item_scrapy函数,这个函数会调用scheduler_next_request函数,获取下一个request。

Scrapy-Redis分布式策略:

首先要说一下Master端和Slaver端

    1. Master
      1. 核心服务器,搭建Redis数据库,不负责爬取,只负责url指纹判断是否重复、request的分配、以及数据的存储
    2. Slaver
      1. 爬虫程序执行端,负责执行爬虫程序,运行过程中提交新的request给Master

首先Slaver端从Master端拿任务(request, url)进行数据爬取,Slaver抓取数据的同时,产生新的request就提交给Master进行处理

Master端只有一个Redis数据库,负责将未处理的request去重和任务分配,将处理后的request加入待爬取的队列,并且存储爬取的数据

将scrapy变成scrapy-redis的过程(前提是已经安装好了scrapy-redis)

  1. 修改settings.py配置文件,最简单的方式就是使用redis来替换当前电脑的内存,并且同时配置好redis数据库相关的内容
    # 这个是需要手动加上的,通过scrapy-redis自带的pipeline将item存入redis中
    ITEM_PIPELINES = {'scrapy_redis.pipelines.RedisPipeline': 400
    }
    # 启动redis自带的去重
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    # 启用调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 是否在关闭spider的时候保存记录
    SCHEDULER_PERSIST = True
    # 使用优先级调度请求队列(默认使用)
    SCHEDULER_QUEUE_CLASS = 
     'scrapy_redis.queue.SpiderPriorityQueue'
    # 指定redis的地址和端口,有密码的需要加上密码
    REDIS_HOST = '127.0.0.1'
    REDIS_PORT = '6379'
    # 如果你的redis设了密码就需要加上密码,
    REDIS_PARAMS = {
        'password': '123456',
    }
    

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK