Scrapy框架 增量式、分布式爬虫

文章目录

    • Scrapy框架
      • 1.增量爬虫
      • 2.分布式爬虫

Scrapy框架

1.增量爬虫

  • 实现思路
利用redis集合数据类型
1.获取到url后进行判断 是否重复???
2.第一次爬取到数据,爬取完成写入该记录...
(两个点: 必须要没有爬过的数据 在这个基础之上做
(1)数据准备入到管道之前
(2)数据入库之前
)
优点:
Redis查询速度快 相比: Mysql 文件 Mongdb ...

  • 信号代码实现 配置打开关闭
@classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        s = cls()
        s._set_crawler(crawler)  # -> 参考父类中的写法
        # 定义两个信号
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(s.spider_closed, signal=signals.spider_closed)
        return s

    def spider_opened(self, spider):
        self.conn = redis.Redis(host="127.0.0.1", port=6379, db=3, password="123456", decode_responses=True)

    def spider_closed(self, spider):
        self.conn.save()  # 存储到硬盘
        self.conn.close()  # 关闭连接
  • 集合去重逻辑
if self.conn.sismerber(key, href?):
    continue

   成功请求获取数据 
	添加该记录 url相当于就是主键  or 管道pipelines设置  process_item()
    open_spider/ close_spider 两个hook方法 上下文管理
    self.conn.sadd(key, url) 

yield {
    xxx: yyy
}
  • 定时任务 while + 进程(scrapy会帮我们自动杀死process)
import time
from scrapy.cmdline import execute
from multiprocessing import Process
from datetime import datetime
def task():
    # sys.exit(0)
    execute("scrapy crawl fuck".split())

if __name__ == '__main__':
    # 定时任务 time.seep()
    while 1:
        process  = Process(target=task)
        process.start()
        print(f"正在等待执行中..... 当前时间=====> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        time.sleep(60 * 2) # 两分钟一次
        process.join()

2.分布式爬虫

Scrapy框架 增量式、分布式爬虫_第1张图片

  • 核心思想
利用多个scrapy程序对同一个任务进行分解,模拟出多个worker干同一任务的场景。直到完成所有的任务。
1.单个scrapy实现去重逻辑: set() + 指纹sha1 创建request的时候设置的是否去重?
2.多个scrapy实现去重逻辑: 共享一个Redis 集合 + 队列(列表lpush brpop) 消息队列
针对处理我们的耗时任务 各个worker之间是独立的不会产生影响!

核心: 依赖Redis  MQ其实也可以 Kafka Celery 处理异步任务的框架 
(1)去重集合
(2)请求队列
  • 简单配置Redis分布式爬虫

    # settings.py
    # 调度器类
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 过滤器类.
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    #  save操作
    SCHEDULER_PERSIST = True 
    # 管道 数据存储在Redis里面
    ITEM_PIPELINES = {
        # 配置redis的pipeline  可用可不用
        'scrapy_redis.pipelines.RedisPipeline': 301  
    }
    # Redis server
    REDIS_HOST = "localhost"
    REDIS_PORT = 6379
    REDIS_DB = 4
    REDIS_PARAMS = {
        "password": ""
    }
    
    # spider文件 爬虫核心文件...
    pip install scrapy-redis
    1.更换继承关系
    2. 干掉这个start_urls
    3. 更换为redis_key 起始url,相当于就是任务id ... 
    [注意] 给Redislpsuh的时候注意推url  该url 请求后的逻辑会返回resp 
    数据处理的逻辑必须一致!!!
    
    from scrapy_redis.spiders import RedisSpider
    class XxxSpider(RedisSpider):
        name = "nb_cqie"
        redis_key = "cqie_pro:item"
    

    [注意]

Scrapy框架 增量式、分布式爬虫_第2张图片

  • 调度器源码分析

    • 初始化调度器
      Scrapy框架 增量式、分布式爬虫_第3张图片

    • 核心代码执行流程
      Scrapy框架 增量式、分布式爬虫_第4张图片
      重复过滤器对象 几个队列类…

    • open函数
      Scrapy框架 增量式、分布式爬虫_第5张图片

Scrapy框架 增量式、分布式爬虫_第6张图片

close

Scrapy框架 增量式、分布式爬虫_第7张图片

​ 入队逻辑
Scrapy框架 增量式、分布式爬虫_第8张图片

Scrapy框架 增量式、分布式爬虫_第9张图片

Scrapy框架 增量式、分布式爬虫_第10张图片

指纹运算 代码分析: 参数 request 请求对象: url method body 等等进行签名

Scrapy框架 增量式、分布式爬虫_第11张图片

Scrapy框架 增量式、分布式爬虫_第12张图片

Scrapy框架 增量式、分布式爬虫_第13张图片

Scrapy框架 增量式、分布式爬虫_第14张图片

请求对象里面一般携带了哪些参数????
request = Request(url, headers, callback, dont_filter,cookies,method, meta,)
入队逻辑: 根据dont_filter判断 短路逻辑   xx and xxx 
        if not request.dont_filter and self.df.request_seen(request): 
            return False
代码解读:
(1) dont_filter False => 去重
not request.dont_filter => True 去重就判断是否是否重复???
执行: self.df.request_seen(request)  过滤器判断
1.如果指纹存在 True 条件: True and True : return False 跳过

2.如果指纹不存在 None/False 条件: True and False : return True 继续请求
request_seen() 校验该请求是否已经请求过了 :返回True/Fasle
指纹:存储在内存里面 集合数据类型自动去重: 算法:sha1 .....

伪代码模型:
if 是否去重 and 判断是否存在(根据指纹,set()): True 执行判断逻辑
	return Fasle 不玩了...
继续干...
将该请求添加到请求队列 给引擎 然后下载器 网络请求....

  • Pycharm配置并行程序

    • 清空运行栈

Scrapy框架 增量式、分布式爬虫_第15张图片

  • 重新配置运行
    Scrapy框架 增量式、分布式爬虫_第16张图片

  • 模板允许并行执行
    Scrapy框架 增量式、分布式爬虫_第17张图片

  • 并行 多个进程同时执行

Scrapy框架 增量式、分布式爬虫_第18张图片

  • 推送任务

    lpush key cqie_pro:item https://www.cqie.edu.cn/html/3/tzgg/Index.shtml
    
    

    程序执行结果:

Scrapy框架 增量式、分布式爬虫_第19张图片

Redis存储数据:

Scrapy框架 增量式、分布式爬虫_第20张图片

Redis分布式爬虫效果:
(1)你只需要往里面put任务基于消息队列 scrapy程序监听到消息后 直接执行 各个节点独立。
(2)支持断点续爬 和增量爬虫本质一样,利用Redis set数据结构 自动过滤掉重复请求
(3) 共享调度器 => Redis
(4) 同步 -> 异步

  • 版本

    pip install scrapy == 2.5.1
    pip install scrapy-redis==0.7.2
    

布隆过滤器:

​ 平时, 我们如果需要对数据进行去重操作可以有以下方案:

1. 直接用set集合来存储url. (最low的方案)
2. 用set集合存储hash过的url. scrapy默认
3. 用redis来存储hash过的请求, scrapy-redis默认就是这样做的. 如果请求非常非常多. redis压力是很大的.
4. 用布隆过滤器. 

布隆过滤器的原理: 其实它里面就是一个改良版的bitmap. 何为bitmap,.

假设我提前准备好一个数组, 然后把源数据经过hash计算. 会计算出一个数字. 我们按照下标来找到该下标对应的位置. 然后设置成1.

a = 李嘉诚
b = 张翠山
....

[0],[0],[0],[0],[0],[0],[0],[0],[0]  10个长度数组

hash(a) => 3
hash(b) => 4

[0],[0],[0],[1],[1],[0],[0],[0],[0] 
# 我想找'张三'
hash('张三') => 6

# 去数组中找6位置的数字。 是0,则不存在'张三'

# 找的时候依然执行该hash算法. 然后直接去找对应下标的位置看看是不是1. 是1就有, 不是1就没有

这样有个不好的现象. 容易误判. 如果hash算法选的不够好. 很容易搞错. 那怎么办. 多选几个hash算法

a = 李嘉诚
b = 张翠山

[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]

hash1(a) = 3
hash2(a) = 4

hash1(b) = 2
hash2(b) = 5

[0],[0],[1],[1],[1],[1],[0],[0],[0],[0]

# 找的时候, 重新按照这个hash的顺序, 在重新执行一遍. 依然会得到2个值. 分别去这两个位置看是否是1. 如果全是1, 就有,  如果有一个是0, 就没有. 

在scrapy-redis中想要使用布隆过滤器是非常简单的. 你可以自己去写这个布隆过滤器的逻辑. 不过我建议直接用第三方的就可以了


# 安装布隆过滤器
pip install scrapy_redis_bloomfilter
# 配置即可...
# 去重类,要使用 BloomFilter 请替换 DUPEFILTER_CLASS
DUPEFILTER_CLASS = "scrapy_redis_bloomfilter.dupefilter.RFPDupeFilter"
# 哈希函数的个数,默认为 6,可以自行修改
BLOOMFILTER_HASH_NUMBER = 6
# BloomFilter 的 bit 参数,默认 30,占用 128MB 空间,去重量级 1 亿
BLOOMFILTER_BIT = 30	
```python

# 安装布隆过滤器
pip install scrapy_redis_bloomfilter
# 配置即可...
# 去重类,要使用 BloomFilter 请替换 DUPEFILTER_CLASS
DUPEFILTER_CLASS = "scrapy_redis_bloomfilter.dupefilter.RFPDupeFilter"
# 哈希函数的个数,默认为 6,可以自行修改
BLOOMFILTER_HASH_NUMBER = 6
# BloomFilter 的 bit 参数,默认 30,占用 128MB 空间,去重量级 1 亿
BLOOMFILTER_BIT = 30	

你可能感兴趣的:(scrapy,分布式,爬虫,python,redis)