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

 

posted @ 2021-07-11 22:35  千叶千影  阅读(329)  评论(0编辑  收藏  举报