引子 :  scrapy框架能否自己实现分布式 ? 

  - 不可以 

    -  原因一 : 因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多态机器无法分配 start_urls 列表中的url . (多台机器无法共享同一个调度器)

    -  原因二 : 多台机器爬取到的数据无法通过一个管道对数据进行统一的数据持久化存储(多台机器无法共享同一个管道),就会造成数据的大量重复.

 

1 . 组件实现   

  - 在scrapy框架中做分布式爬虫基于scrapy-redis组件进行的

  - 实现方法 : 

    - 基于该组件的RedisSpider类

    - 基于该组件的RedisCrawlSpider

2 . 分布式实现的流程 : 

  2.1  下载scrapy-redis组件 : pip install scrapy-redis

  2.2  redis 配置文件的修改 : 

56行 - 注释该行:bind 127.0.0.1,表示可以让其他ip访问redis

76行 - 将yes该为no:protected-mode no,表示可以让其他ip操作redis

# redis-windows-conf 文件

  2.3  创建工程 :  scrapy startproject xxx(项目名)

     创建爬虫文件 : 继承与 --> RedisCrawlSpider  /  RedisSpider

     scrapy genspider -t crawl xxx www.xxx.com  (我们使用的是RedisCrawlSpider  )

  2.4  对爬虫文件中的属性进行修改 : 

    #- 导包: from scrapy_redis.spiders import RedisCrawlSpider
    #- 将当前爬虫文件的父类设置成    RedisCrawlSpider
    #- 将起始url列表替换成  redis_key = 'xxx'(调度器队列的名称)

爬虫文件 fenbu_hh.py  

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
from fenbu.items import FenbuItem

class FenbuHhSpider(RedisCrawlSpider):
    name = 'fenbu_hh'
    # allowed_domains = ['www.xxx.com']
    # start_urls = ['http://www.xxx.com/']
    """
    分布式爬虫,代码编写好后可以放到分布式集群中,集群中运行的程序时同一个,如果程序中有起始
    的url,就表示所有的机器都url,要去请求这个url进行解析-->会导致更多的重复数据
    做分布式就是要其中的某台电脑有url,从url中解析出子url,扔到调度器的队列中
    因为在调度器中,经过去重的url才会放到队列中.
    但是起始url还不能没有,就把起始url当成全局的,让集群中的机器其抢,谁抢到谁请求解析
    保证起始url只被请求一次
    https://dig.chouti.com/
    """
    # 可以被共享的调度器的名称,
    # 当程序执行之后,手动向调度器的对类中放一个起始url

redis_key = 'chouti' # 调度器队列的名称 ********** rules = ( Rule(LinkExtractor(allow=r'/all/hot/recent/\d+'), callback='parse_item', follow=True), ) def parse_item(self, response): div_list = response.xpath('//div[@class="item"]') for div in div_list: title = div.xpath('./div[4]/div[1]/a/text()').extract_first() author = div.xpath('/div[4]/div[2]/a[4]/b/text()').extract_first() # 实例化item对象进行封装 item = FenbuItem() item['title'] = title item['author'] = author yield item

  2.5  在配置文件中进行配置 

    注意 : UA , ROBOTSTXT_OBEY = False   不要忘记也要修改

#- 使用组件中封装好的可以被共享的管道类:
        ITEM_PIPELINES = {
            'scrapy_redis.pipelines.RedisPipeline': 400
            }
#- 配置调度器(使用组件中封装好的可以被共享的调度器)
        # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
        # 使用scrapy-redis组件自己的调度器
        SCHEDULER = "scrapy_redis.scheduler.Scheduler"
        # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
        SCHEDULER_PERSIST = True

#- 指定存储数据的redis:
        REDIS_HOST = 'redis服务的ip地址'
        REDIS_PORT = 6379
        REDIS_ENCODING = ‘utf-8’
        REDIS_PARAMS = {‘password’:’123456’}

3 . 开启redis服务器 : redis-server 

  开始redis客户端 : redis-cli

4 . 运行爬虫文件 : scrapy runspider fenbu_hh.py

  注意 : 要cd 到 spiders 里面在执行

基于scrapy-redis两种形式的分布式爬虫

在执行之后,会夯住

基于scrapy-redis两种形式的分布式爬虫

5 . 向调度器队列中扔一个起始url (在redis的客户端操作) : 

  lpush chouti https://dig.chouti.com/      (lpush redis_key属性值 起始url)

 基于scrapy-redis两种形式的分布式爬虫

这个时候,爬虫文件就会继续执行 

6 . scrapy-redis 的调度器如何实现任务的深度优先和广度优先

  在scrapy-redis中,默认的是 深度优先

  如果想变成 广度优先,在配置文件中写入

- DEPTH_PRIORITY = 1\n,
- SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'\n,
- SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'\n,

  深度优先和广度优先的对比

    - 广度优先:不全部保留结点,占用空间少;运行速度慢\n,
    - 深度优先:保留全部结点,占用空间大;运行速度快