• scrapy-redis实现分布式爬虫


    什么是分布式爬虫

      • 分布式爬虫就是多台计算机上都安装爬虫程序,重点是联合采集。单机爬虫就是只在一台计算机上的爬虫。

      • 其实搜索引擎都是爬虫,负责从世界各地的网站上爬取内容,当你搜索关键词时就把相关的内容展示给你,只不过他们那都是灰常大的爬虫,爬的内容量也超乎想象,也就无法再用单机爬虫去实现,而是使用分布式了,一台服务器不行,我来1000台。我这么多分布在各地的服务器都是为了完成爬虫工作,彼此得通力协作才行啊,于是就有了分布式爬虫

      • 单机爬虫的问题:

        • 一台计算机的效率问题
        • IO 的吞吐量,传输速率也有限
      • 多爬虫问题

        • 多爬虫要实现数据共享
          • 比如说一个爬取了某个网站,下载了哪些内容,其他爬虫要知道,以避免重复爬取等很多问题,所以要实现数据共享
        • 在空间上不同的多台机器,可以成为分布式
      • 多爬虫条件:

        • 需要共享队列
        • 去重,让多个爬虫不爬取其他爬虫爬取过的爬虫
      • 理解分布式爬虫:

        • 假设上万的 url 需要爬取,有 100 多个爬虫,分布在全国不同的城市
        • url 被分给不同的爬虫,但是不同爬虫的效率又是不一样的,所以说共享队列,共享数据,让效率高的爬虫多去做任务,而不是等着效率低的爬虫
      • Redis

        • Redis 是完全开源免费的,遵守BSD协议,是一个高性能的 key-value 数据库
        • 内存数据库,数据存放在内存
        • 同时可以落地保存到硬盘
        • 可以去重
        • 可以把 Redis 理解成一共 dict,set,list 的集合体
        • Redis 可以对保存的内容进行生命周期
        • Redis 教程:Redis 教程 - 菜鸟教程
      • 内容保存数据库

        • MongoDB,运行在内存,数据保存在硬盘
        • MySQL

    一、 分布式爬虫Master的settings:

    # 使用scrapy-redis里的去重组件,不使用scrapy默认的去重方式
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    # 使用scrapy-redis里的调度器组件,不使用默认的调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 允许暂停,redis请求记录不丢失
    SCHEDULER_PERSIST = True
    # 默认的scrapy-redis请求队列形式(按优先级)
    #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
    # 栈形式,请求先进后出
    #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
    # 队列形式,请求先进先出
    SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
    # 打开请求延时为1秒
    DOWNLOAD_DELAY = 1
    # 配置redis的主机号
    REDIS_HOST = '127.0.0.1'
    # 配置redis的端口号
    REDIS_PORT = 6379
    
    #也可以通过下面这种方法设置redis地址 端口和密码,一旦设置了这个,则会覆盖上面所设置的REDIS_HOST和REDIS_HOST
    #root用户名,redis_pass:你设置的redis验证密码,xxxx:你的主机ip
    # REDIS_URL = 'redis://root:redis_pass@xxx.xx.xx.xx:6379'
    REDIS_ENCODING = 'utf-8'

    master其他设置:spider文件

    1.需要更改继承的类
        from scrapy_redis.spiders import RedisSpider

    2.注释掉start_urls

    3.在爬虫目录下新创建一个redis_urls.py文件,放所有的URL到redis数据库的列表中

    4.回到爬虫文件中,写一个redis_key = '列表的key'

    redis_urls.py

    二、分布式爬虫Slave的settings:

    前面的和master都一样
    
    #配置redis主机名
    REDIS_HOST = 'master的IP'
    #配置redis端口号
    REDIS_PORT = 6379
    # 若有密码则一样和master保持一致
    ...

    slave其他配置:

    1、需要删除redis_urls文件

    2、如果数据是存到master的MongoDB数据库,需要到pipelines文件中将host改成master的ip,数据库改成master的,集合也改成master的。


    注意:scrapy-redis存在空跑问题

    1.在master和slave项目目录下,新建一个extensions.py文件,写如下代码:

    # spider_idle信号只有在爬虫队列为空时才会被触发, 触发间隔为5s。
    
    import logging
    
    from scrapy import signals
    from scrapy.exceptions import NotConfigured
    
    logging = logging.getLogger(__name__)
    
    
    class RedisSpiderSmartIdleClosedExensions(object):
    
        def __init__(self, idle_number, crawler):
            self.crawler = crawler
            self.idle_number = idle_number
            self.idle_list = []
            self.idle_count = 0
    
        @classmethod
        def from_crawler(cls, crawler):
            # first check if the extension should be enabled and raise
    
            # NotConfigured otherwise
    
            if not crawler.settings.getbool('MYEXT_ENABLED'):
                raise NotConfigured
    
            if not 'redis_key' in crawler.spidercls.__dict__.keys():
                raise NotConfigured('Only supports RedisSpider')
    
            # get the number of items from settings
    
            idle_number = crawler.settings.getint('IDLE_NUMBER', 360)
    
            # instantiate the extension object
    
            ext = cls(idle_number, crawler)
    
            # connect the extension object to signals
    
            crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
    
            crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)
    
            crawler.signals.connect(ext.spider_idle, signal=signals.spider_idle)
    
            return ext
    
        def spider_opened(self, spider):
            spider.logger.info("opened spider {}, Allow waiting time:{} second".format(spider.name, self.idle_number * 5))
    
        def spider_closed(self, spider):
            spider.logger.info(
                "closed spider {}, Waiting time exceeded {} second".format(spider.name, self.idle_number * 5))
    
        def spider_idle(self, spider):
            # 程序启动的时候会调用这个方法一次,之后每隔5秒再请求一次
            # 当持续半个小时都没有spider.redis_key,就关闭爬虫
            # 判断是否存在 redis_key
            if not spider.server.exists(spider.redis_key):
                self.idle_count += 1
            else:
                self.idle_count = 0
    
            if self.idle_count > self.idle_number:
                # 执行关闭爬虫操作
                self.crawler.engine.close_spider(spider, 'Waiting time exceeded')

    2.打开settings.py文件中EXTENSIONS的注释,将Telent的注释掉,换上:

    '项目名.extensions.RedisSpiderSmartIdleClosedExensions': 500

    3.配置settings.py文件:

    # 开启扩展
    MYEXT_ENABLED = True
    # 每5秒就检测一次,检测10次(50秒),如果url还为空,那么就结束爬虫程序
    IDLE_NUMBER = 10

    最后Slave尝试连接Master
    尝试连接 mongo:mongo --host masterIP --port 27017

    尝试连接master的redis数据库:redis-cli -h masterIP

    ps:master的redis数据库配置文件需要做如下更改:
    1.将bind 127.0.0.1 注释掉

    2.将protected-mode yes 改为 protected-mode no

    天青色等烟雨而我在等你!
  • 相关阅读:
    Linux的五个查找命令
    jquery mobile图片自适应屏幕
    JS定时刷新页面及跳转页面
    JavaScript-每隔5分钟执行一次ajax请求的实现方法
    jquerymobile-可折叠内容(Collapsible content)
    MVC入门教程-视图中的Layout使用
    如何利用 _ViewStart.cshtml对页面添加代码?
    检测到有潜在危险的Request.Form值
    varchar(n)和varchar(max)有什么区别
    报错:System.Data.Entity.Infrastructure.DbUpdateException 更新条目时出错
  • 原文地址:https://www.cnblogs.com/Liu928011/p/14999692.html
Copyright © 2020-2023  润新知