zoukankan      html  css  js  c++  java
  • scrapy_redis使用介绍

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

    (1)dupefilter——URL去重规则(被调度器使用)

    (2)scheduler——调度器

    (3)pipeline——数据持久化

    一、安装redis

    去官网下载redis并安装到电脑上

    二、安装scrapy_redis组件

    打开终端输入:pip install scrapy-redis  即可 (os/linux)

    组件默认被安装在相应的Python文件夹的site-packages里面。如/usr/local/lib/python3.7/site-packages/scrapy_redis

    三、scrapy_redis功能详解

    (一)URL去重

    1、源码  /usr/local/lib/python3.7/site-packages/scrapy_redis/dupefilter.py

    setting.py中的配置信息:

    # redis配置
    REDIS_HOST = "127.0.0.1"
    REDIS_PORT = 6379
    REDIS_PARAMS = {}
    REDIS_ENCODING = "utf-8"
    
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    DUPEFILTER_KEY = "dupefilter:%(timestamp)s"

    2、重写dupefilter

    可以根据自己的需求自定制dupefilter

    在spiders的同级目录新建文件dupefilter.py,写入代码:

    """
    重写dupefilter
    """
    from scrapy_redis.dupefilter import RFPDupeFilter
    from scrapy_redis.connection import get_redis_from_settings
    
    
    class MyDupeFilter(RFPDupeFilter):
        @classmethod
        def from_settings(cls, settings):
            server = get_redis_from_settings(settings)
            key = "my_scrapy_2_dupfilter"  # 重写key
            debug = settings.getbool('DUPEFILTER_DEBUG')
            return cls(server, key=key, debug=debug)

    在settings.py中进行相关配置:

    # redis配置
    REDIS_HOST = "127.0.0.1"   # 主机
    REDIS_PORT = 6379  # 端口号
    REDIS_PARAMS = {}  # 连接参数
    REDIS_ENCODING = "utf-8"  # 编码规则
    #配置自己的dupefilter路径
    DUPEFILTER_CLASS = "my_scrapy_2.dupefilter.MyDupeFilter"

    (二)调度器

    1、广度优先和深度优先

    (1)栈——后进先出——广度优先——LifoQueue(列表)

    (2) 队列——先进先出——深度优先——FifoQueue(列表)

    (3) 优先级集合——PriorityQueue(有序集合)

    2、在settings.py中:

    # redis配置
    REDIS_HOST = "127.0.0.1"
    REDIS_PORT = 6379
    REDIS_PARAMS = {}
    REDIS_ENCODING = "utf-8"
    
    # 去重规则
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    # 调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'  # 默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
    SCHEDULER_QUEUE_KEY = '%(spider)s:requests'  # 调度器中请求存放在redis中的key  chouti:requests
    SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"  # 对保存到redis中的数据进行序列化,默认使用pickle
    SCHEDULER_PERSIST = True  # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
    SCHEDULER_FLUSH_ON_START = True  # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
    # SCHEDULER_IDLE_BEFORE_CLOSE = 10  # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)。
    SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter'  # 去重规则,在redis中保存时对应的key
    SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'  # 去重规则对应处理的类
    
    DEPTH_PRIORITY = -1  # 如果是使用优先级集合(PriorityQueue)就用做该配置参数  DEPTH_PRIORITY可以设为-1或者1 

    (三)数据持久化

    1、源码

    以爬取抽屉新热榜的新闻标题与连接为例:

    爬虫 chouti.py:

    # -*- coding: utf-8 -*-
    """
    爬取抽屉新热榜的新闻标题以及url 并保存
    """
    import scrapy
    from scrapy.http import Request
    from ..items import MyScrapy3Item
    
    
    class ChoutiSpider(scrapy.Spider):
        name = 'chouti'
        allowed_domains = ['chouti.com']
        start_urls = ['http://chouti.com/']
    
        def parse(self, response):
            # print(response, response.request.priority, response.meta.get('depth'))
            items = response.xpath("//div[@id='content-list']/div[@class='item']")
            for item in items:
                title = item.xpath(".//div[@class='part1']/a/text()").extract_first().strip()  # 标题
                href = item.xpath(".//div[@class='part1']/a/@href").extract_first().strip()  # 连接
                yield MyScrapy3Item(title=title, href=href)  # yield一个item对象
            # 翻页
            page_list = response.xpath('//*[@id="dig_lcpage"]//a/@href').extract()
            for url in page_list:
                url = "https://dig.chouti.com" + url
                yield Request(url=url, callback=self.parse)

    items.py:

    import scrapy
    
    
    class MyScrapy3Item(scrapy.Item):
        title = scrapy.Field()
        href = scrapy.Field()

    settings.py中做相关的配置:

    ITEM_PIPELINES = {
        "scrapy_redis.pipelines.RedisPipeline": 300,  # 设置使用scrapy_redis的持久化类
    }

    # -----------其他配置----------------------
    DEPTH_LIMIT = 2  # 爬取深度


    # redis配置 (必须的)
    REDIS_HOST = "127.0.0.1"
    REDIS_PORT = 6379
    REDIS_PARAMS = {}
    REDIS_ENCODING = "utf-8"

    # 去重规则
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

    # 调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue' # 默认使用优先级队列(默认),其他:PriorityQueue(有序集合),FifoQueue(列表)、LifoQueue(列表)
    SCHEDULER_QUEUE_KEY = '%(spider)s:requests' # 调度器中请求存放在redis中的key chouti:requests
    SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat" # 对保存到redis中的数据进行序列化,默认使用pickle
    SCHEDULER_PERSIST = True # 是否在关闭时候保留原来的调度器和去重记录,True=保留,False=清空
    SCHEDULER_FLUSH_ON_START = True # 是否在开始之前清空 调度器和去重记录,True=清空,False=不清空
    # SCHEDULER_IDLE_BEFORE_CLOSE = 10 # 去调度器中获取数据时,如果为空,最多等待时间(最后没数据,未获取到)
    SCHEDULER_DUPEFILTER_KEY = '%(spider)s:dupefilter' # 去重规则,在redis中保存时对应的key
    SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter' # 去重规则对应处理的类
     

    在项目根目录新建文件start_chouti.py用于运行爬虫(也可以直接在终端输命令来运行):

    from scrapy.cmdline import execute
    
    if __name__ == "__main__":
        execute(["scrapy", "crawl", "chouti", "--nolog"])

    可以新建一个py文件用于查看保存在Redis中的数据:

    # 3种方式查看数据

    import
    redis conn = redis.Redis(host="127.0.0.1", port=6379) # conn.flushall() # 清空Redis print(conn.keys()) # 查看所有key [b'chouti:dupefilter', b'chouti:items']
    # 1、获取指定范围的数据 # res = conn.lrange('chouti:items', 0, 3) # 获取持久化的数据 取3条 # print(res) """
    结果:
    [b'{"title": "\u3010\u56fe\u96c6\u30112018\u5e74\u5ea6\u5929\u6587\u6444\u5f71\u5e08\u5927\u8d5b\u83b7\u5956\u4f5c\u54c1\u516c\u5e03", "href": "https://mp.weixin.qq.com/s/eiWj7ky53xEDoMRFXC1EGg"}', b'{"title": "\u3010\u201c\u4eba\u76f4\u5230\u5165\u571f\u4e3a\u5b89\u90a3\u4e00\u5929\uff0c\u90fd\u5728\u8d70\u53f0\u9636\u201d \u3011\u674e\u548f\u5728\u63a5\u53d7\u91c7\u8bbf\u65f6\u66fe\u8fd9\u6837\u5f62\u5bb9\u81ea\u5df1\u7684\u4eba\u751f\uff1a\u201c\u4eba\u76f4\u5230\u5165\u571f\u4e3a\u5b89\u90a3\u4e00\u5929\uff0c\u90fd\u5728\u8d70\u53f0\u9636\u3002\u8ddf\u767b\u9ec4\u5c71\u4e00\u6837\uff0c\u767b\u7684\u65f6\u5019\u4f60\u4e0d\u89c9\u5f97\u6709\u4e91\uff0c\u5230\u4e00\u5b9a\u9ad8\u5ea6\u7684\u65f6\u5019\uff0c\u65c1\u8fb9\u6709\u4eba\u63d0\u9192\u4f60\u56de\u5934\u770b\u4e00\u4e0b\uff0c\u4e91\u5c31\u5728\u773c\u524d\u3002\u201d", "href": "https://mp.weixin.qq.com/s/erLgWmL1GhpyWqwOTIlRvQ"}', b'{"title": "\u3010\u6e38\u620f\u673a\u5236 \u6e17\u900f\u5e76\u6e10\u6e10\u5851\u9020\u4e86\u73b0\u5b9e\u4e16\u754c\uff0c\u4f60\u662f\u5426\u4e5f\u4e00\u6837\u8ba4\u4e3a\u7406\u6240\u5f53\u7136\uff1f\u3011\u5728\u667a\u80fd\u624b\u673a\u666e\u53ca\u4ee5\u540e\uff0c\u79fb\u52a8\u6280\u672f\u80fd\u591f\u4e0e\u73b0\u5b9e\u4e16\u754c\u53d1\u751f\u8d8a\u6765\u8d8a\u591a\u7684\u4ea4\u4e92\uff0c\u56e0\u6b64\u6e38\u620f\u5316\u7684\u5c1d\u8bd5\u5e76\u6ca1\u6709\u51cf\u5c11\u53cd\u800c\u589e\u591a\u4e86\u3002\u6709\u6bcf\u5929\u8bb0\u5f55\u4f60\u7684\u6b65\u884c\u8ddd\u79bb\uff0c\u7136\u540e\u9881\u53d1\u5956\u7ae0\u7684\u3002\u6709\u8bb0\u5f55\u4f60\u7684\u4e60\u60ef\uff0c\u5e76\u53ef\u4ee5\u5efa\u8bbe\u4e00\u5ea7\u57ce\u5e02\u7684\u3002", "href": "http://www.qdaily.com/articles/57753.html"}', b'{"title": "\u3010\u53c8\u5931\u4e00\u57ce\uff01\u9ed8\u514b\u5c14\u7684\u201c\u9ec4\u91d1\u914d\u89d2\u201d\u5728\u9ed1\u68ee\u5dde\u906d\u9047\u60e8\u8d25\u3011\u4eca\u5e74\u4e09\u6708\u8270\u96be\u5b8c\u6210\u7b2c\u56db\u6b21\u7ec4\u9601\u7684\u5fb7\u56fd\u603b\u7406\u9ed8\u514b\u5c14\uff0c\u572810\u6708\u5fb7\u56fd\u4e24\u4e2a\u5173\u952e\u5dde\u2014\u2014\u5df4\u4f10\u5229\u4e9a\u5dde\u548c\u9ed1\u68ee\u5dde\u7684\u9009\u4e3e\u4e2d\uff0c\u63a5\u8fde\u906d\u9047\u5386\u53f2\u6027\u60e8\u8d25\u3002\u9ed8\u514b\u5c14\u7684\u201c\u9ec4\u91d1\u914d\u89d2\u201d\u2014\u2014\u793e\u6c11\u515a\uff08SPD\uff09\u5728\u4e24\u6b21\u9009\u4e3e\u4e2d\u7684\u5f97\u7968\u7387\u5448\u73b0\u81ea\u7531\u843d\u4f53\u72b6\u6001\u3002", "href": "https://wallstreetcn.com/articles/3428455"}'] """
    # 2、一条一条的将数据取走
    # item = conn.lpop('chouti:items') 
    # print(item)

    """
    结果:
    b'{"title": "\u3010\u56fe\u96c6\u30112018\u5e74\u5ea6\u5929\u6587\u6444\u5f71\u5e08\u5927\u8d5b\u83b7\u5956\u4f5c\u54c1\u516c\u5e03", "href": "https://mp.weixin.qq.com/s/eiWj7ky53xEDoMRFXC1EGg"}' """

    # 3、做成一个生产者-消费者模型
    while True:
    item = conn.blpop('chouti:items') # 一条一条的将数据取走 如果没有了就阻塞住
    print(item)
     

    通过使用scrapy_redis的持久化数据功能,可以将生产数据和获取数据作为两件互不影响的事情并发的运行。

    2、如果还想要将数据存入其他地方,可以继承和重写scrapy_redis的pipelines

    四、起始URL的定制

    让爬虫像永动机一样一直处于备战状态,如果没有请求就处于等待状态,当有新的URL进来时就开始爬取数据。

    在爬虫文件中:

    """
    爬取抽屉新热榜的新闻标题以及url 并保存
    让爬虫一直爬数据,如果没有就处于等待状态
    """
    from scrapy_redis.spiders import RedisSpider
    
    
    class ChoutiSpider(RedisSpider):  # 继承RedisSpider
        name = 'chouti'
        allowed_domains = ['chouti.com']
    
        def parse(self, response):
            print(response)

    配置文件中:

    # 起始url
    REDIS_START_URLS_AS_SET = True  # True:在Redis里面按照集合去存,False:按照列表来存储
    START_URLS_KEY = '%(name)s:start_urls'  # 在源码中默认的起始URL的key为 chouti:start_urls

    再写一个py文件来设置url:

    import redis
    
    conn = redis.Redis(host="127.0.0.1", port=6379)
    
    # conn.flushall()  # 清空Redis
    
    print(conn.keys())  # 查看所有key  [b'chouti:start_url']
    
    item = conn.sadd("chouti:start_url", "https://dig.chouti.com/r/pic/hot/1")  # 设置起始url
    print(item)
  • 相关阅读:
    Selenium(Python)等待元素出现
    java文件的I/O
    Map的四种遍历方式
    模板类实现链表
    字符串相关库函数使用
    动态规划之背包问题
    最长递增子序列
    深度优先搜索(DFS),逃离迷宫
    素数环问题(递归回溯)
    枚举(百鸡问题)
  • 原文地址:https://www.cnblogs.com/yanlin-10/p/9869110.html
Copyright © 2011-2022 走看看