• 爬虫之scrapy-redis


    redis分布式部署

    scrapy框架是否可以自己实现分布式?

    不可以原因有两点

    其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的url。(多台机器无法共享同一个调度器)
    
    其二:多台机器爬取到的数据无法通过同一个管道对数据进行统一的数据持久出存储。(多台机器无法共享同一个管道)

    基于scrapy-redis组件的分布式爬虫

    scrapy-redis组件中为我们封装好了可以被多台机器共享的调度器和管道,我们可以直接使用并实现分布式数据爬取

    实现方式:

    1.基于该组件的RedisSpider类 ==》基于该组件的RedisCrawlSpider类

    分布式实现流程:

    一.修改爬虫文件
        1.导入scrapy-redis模块:from scrapy_redis.spiders import RedisSpider
        2.将当前爬虫类的父类修改成RedisSpider
        3.将allowed_domains和start_urls进行删除
        4.添加一个新的属性redis_key = 'xxx',该属性值表示的就是可以被共享的调度器队列的名称
    二.进行配置文件的配置
        1.保证爬虫文件发起的请求都会被提交到可以被共享的调度器的队列中
            SCHEDULER = "scrapy_redis.scheduler.Scheduler"
       2.确保所有爬虫共享相同的去重指纹
         DUPEFILTER = "scrapy_redis.dupefilter.RFPDuperFilter"
    3.保证爬虫文件提交的item会被存储到可以被共享的管道中 ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 }
      
       4. 在redis中保持scrapy-redis用到的队列,不会清理redis中的队列,从而实现暂停和恢复功能
      SCHEDULER_PERSTST = True


    5.配置最终数据存储的redis数据库 REDIS_HOST = 'redis服务的ip地址' REDIS_PORT = 6379 REDIS_ENCODING = ‘utf-8’ REDIS_PARAMS = {‘password’:’123456’} 6.redis数据库的配置文件进行配置:关闭保护模式和 改bind 0.0.0.0 7.开启redis服务和客户端 8.执行爬虫文件:scrapy runspider xxx.py 9.向调度器队列中仍入一个起始的url:

    代码如下

     1.创建项目和应用

    scrapy start project redisDemo
    cd redisDemo
    scrapy genspider testDemo
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy_redis.spiders import RedisSpider
    from redisDemo.items import RedisdemoItem
    
    '''
    一.修改爬虫文件
        1.导入scrapy-redis模块:from scrapy_redis.spiders import RedisSpider
        2.将当前爬虫类的父类修改成RedisSpider
        3.将allowed_domains和start_urls进行删除
        4.添加一个新的属性redis_key = 'xxx',该属性值表示的就是可以被共享的调度器队列的名称
    二.进行配置文件的配置
        1.保证爬虫文件发起的请求都会被提交到可以被共享的调度器的队列中
            SCHEDULER = "scrapy_redis.scheduler.Scheduler"
        2.保证爬虫文件提交的item会被存储到可以被共享的管道中
        ITEM_PIPELINES = {
         'scrapy_redis.pipelines.RedisPipeline': 400
        }
        3.配置最终数据存储的redis数据库
            REDIS_HOST = 'redis服务的ip地址'
            REDIS_PORT = 6379
            REDIS_ENCODING = ‘utf-8’
            REDIS_PARAMS = {‘password’:’123456’}
        4.redis数据库的配置文件进行配置:关闭保护模式和 改bind 0.0.0.0
        5.开启redis服务和客户端
        6.执行爬虫文件:scrapy runspider xxx.py
        7.向调度器队列中仍入一个起始的url:
    
    '''
    
    
    class TestdemoSpider(RedisSpider):
        name = 'testDemo'
    
        # allowed_domains = ['www.x.com']  # 不注释可能会出问题
        # start_urls = ['http://www.x.com/']  # 起始url需手动输入
        redis_key = 'cmdb'  # 表示的就是可以被共享的调度器队列的名称
    
        url = 'http://db.pharmcube.com/database/cfda/detail/cfda_cn_instrument/%d'  # 所有的url
        page = 1  # 生成所有url的公共变量
    
        def parse(self, response):
            item = RedisdemoItem()  # 实例化item
            item['num'] = response.xpath('/html/body/div/table/tbody/tr[1]/td[2]/text()').extract_first()
            item['company_name'] = response.xpath('//html/body/div/table/tbody/tr[2]/td[2]/text()').extract_first()
            item['company_address'] = response.xpath('/html/body/div/table/tbody/tr[3]/td[2]/text()').extract_first()
    
            yield item
            urls = []
            if self.page < 130000:
                self.page += 1
                new_url = format(self.url % self.page)
                yield scrapy.Request(url=new_url, callback=self.parse)
    testDemo.py
    import scrapy
    
    
    class RedisdemoItem(scrapy.Item):
        # define the fields for your item here like:
        num = scrapy.Field()
        company_name = scrapy.Field()
        company_address = scrapy.Field()
    items.py
    BOT_NAME = 'redisDemo'
    
    SPIDER_MODULES = ['redisDemo.spiders']
    NEWSPIDER_MODULE = 'redisDemo.spiders'
    
    # Crawl responsibly by identifying yourself (and your website) on the user-agent
    # USER_AGENT = 'redisDemo (+http://www.yourdomain.com)'
    
    # Obey robots.txt rules
    ROBOTSTXT_OBEY = False
    USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
    # 使用的是可以被共享的调度器
    # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    # 使用scrapy-redis组件自己的调度器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
    SCHEDULER_PERSIST = True
    
    # 使用scrapy-redis中封装好的可以被共享的管道
    ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 400
    }
    
    # 配置redis
    REDIS_HOST = '192.168.12.57'  # ip
    REDIS_PORT = 6379             # 端口
    REDIS_ENCODING = 'utf-8'      # 编码
    # REDIS_PARAMS = {‘password’:’123456’}  # 密码  没有就不写了
    settings.py

    redis配置

    开启redis服务和客户端
    net start redis
    在爬虫应用目录下执行爬虫文件:(多台机器启动)
    scrapy runspider xxx.py

    需要向调度器队列中仍入一个起始的url:(不要在开启新机器了)
    在redis-cli中输入

    lpush cmdb http://db.pharmcube.com/database/cfda/detail/cfda_cn_instrument/1

    查看内容: lrange testDemo:items 0 -1


    ok,结束

    增量爬取

    无非判断发送请求的url或者在数据存入时候的关键字判断

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from incrementDemo.items import IncrementdemoItem
    from redis import Redis
    import hashlib
    
    class QiubaiSpider(CrawlSpider):
        name = 'qiubai'
        # allowed_domains = ['www.x.com']
        start_urls = ['https://www.qiushibaike.com/text/']
    
        rules = (
            Rule(LinkExtractor(allow=r'/text/page/d+/'), callback='parse_item', follow=True),  # 所有页面
            Rule(LinkExtractor(allow=r'/text/$'), callback='parse_item', follow=True),  # 第一页面
        )
    
        # 创建redis链接对象
        conn = Redis(host='127.0.0.1', port=6379)
    
        def parse_item(self, response):
            div_list = response.xpath('//div[@id="content-left"]/div')  # 获取内容标签列表
    
            for div in div_list:
                item = IncrementdemoItem()
                item['author'] = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first()  # 作者
                item['content'] = div.xpath('.//div[@class="content"]/span/text()').extract_first()  # 内容
    
    
                # 将解析到的数据值生成一个唯一的标识进行redis存储
                source = item['author'] + item['content']
                source_id = hashlib.sha256(source.encode()).hexdigest()
                # 将解析内容的唯一表示存储到redis的data_id中
                ex = self.conn.sadd('data_id', source_id)
    
                if ex == 1:
                    print('该条数据没有爬取过,可以爬取......')
                    yield item
                else:
                    print('该条数据已经爬取过了,不需要再次爬取了!!!')
    爬虫文件

    items和管道

    import scrapy
    
    
    class IncrementdemoItem(scrapy.Item):
        # define the fields for your item here like:
        author = scrapy.Field()
        content = scrapy.Field()
    items.py
    from redis import Redis
    import json
    
    class IncrementdemoPipeline(object):
        conn = None
    
        def open_spider(self, spider):
            self.conn = Redis(host='127.0.0.1', port=6379)
    
        def process_item(self, item, spider):
            dic = {
                'author': item['author'],
                'content': item['content']
            }
            # print(dic)
            self.conn.lpush('qiubaiData', json.dumps(dic))
            return item
    管道
    ROBOTSTXT_OBEY = False
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
    
    ITEM_PIPELINES = {
       'incrementDemo.pipelines.IncrementdemoPipeline': 300,
    }
    settings.py
  • 相关阅读:
    android 发短信
    如何判断一个Div是否可视区域,判断div是否可见
    java arrayCopy
    Java 正则表达式 向前、向后匹配
    postgres 正则表达式
    java 分析方法调用过程
    chrome 模拟点击
    Java获取NTP网络时间
    Android对话框与Activity共存时的异常
    Android代码混淆
  • 原文地址:https://www.cnblogs.com/clbao/p/10279070.html
Copyright © 2020-2023  润新知