分布式爬虫理念

目录

分布式爬虫理念

1.分布式爬虫框架

2.维护爬取队列

3.去重

4.防止中断


1.分布式爬虫框架

Scrapy中有一个本地爬取队列Queue,这个队列是利用depue模块实现的,新的Repuest生成就会被放到队列里,随后被调度器Scheduler调度,交给Downloader执行爬取,简单的调度框架如图:

 

如果两个Scheduler同时从队列里去Repuest,每个Scheduler都有对应的Downloader,那么在宽带足够,正常爬取不考虑队列压力情况下,爬取效应会有什么变化,答案是爬取效率翻倍

这样,Scheduler和Downloader都可以扩展多个,而爬取队列Queue必须从始至终为1个,也就是所谓的共享队列爬取,这样才能保证Scheduler从队列里调度每个Repuest后,其他Scheduler不会重复调度Repuest,就可以做到多个Repuest同步爬取了,这就是分布爬虫的基本雏形

 我们要做的就是多台主机上同时运行爬虫任务协同爬取,协同爬取前提就是共享爬取队列,这样各台主机就不需要各自维护爬取队列,从共享爬取队列中存取Repuest就行了,但是各台主机还是有各自的Scheduler和Dwonloder,所以调度和下载功能分别完成,如果不考虑队列存取性能消耗,爬取效率还会成倍提高。

2.维护爬取队列

爬取队列怎样维护比较好呢?首先考虑就是性能问题,什么数据库存取效率高?我们自然就能想到基于内存存储的Redis,而且Redis支持多种数据结构,例如列表[list]、集合(set)、等,存储的操作也非常简单,所以我们这里采用Redis来维护爬取队列,

实际上这几种数据库存储结构各有千秋

● 列表数据结构有lpush、lpop、rpush、方法,我们可以用它实现一个先进先出式爬取队列,也可  以实现一个先进后出 得到栈式爬取队列。

● 集合的元素是无序且不重的,我们可以非常方便的实现随机排序不重复的爬取队列。

● 有序集合带有分数表示,而Scrapy和Repuest也有优先的控制,所以有序集合我们可以实现一个带有优先级调度的队列。

3.去重

Scrapy有自动去重功能,他的去重使用了python中的集合,这个集合记录了Scrapy和每个Repuest的指纹,这个指纹实际上就是Repuest散列值,我们可以看下Scrapy代码,

import hashlib
def repuest_fingerprint(repuest, include_headers=None):
    if include_headers:
        vif include_headers = tuple(to_bytes(h.lower())
                                     for h in soted(if include_headers))
cache = _fingerprint_cache.setdefault(repuest,{})
if include_headers not in cache:
fp = hashlib. sha1()
fp. update(to_ bytes (request . method))
fp. update(to_ bytes(canonicalize url(request.uIl)))
fp. update(request . bodyor b'')
if include_ headers:
    for hdr in include_ headers:
        if hdr in request .headers:
            fp.update(hdr)
            for v in request.heders.getlist(hdr);
                    fp.update(v)
    cache[include_headers ]fp.hexdigest()
return cache[include_headers ]

repuest_fingerpront就是计算Repuest指纹的方法,内部颁发就是使用hashlib的sha1方法。计算的字段包括Repuest的URL、Headers这几部分内容,只要有一点不同,那么计算的结果就不同,计算得到的结果加密后的字符串,也就是指纹,每个Repuest都有独有的指纹,指纹就是一个字符串,判断字符串比判定Repuest对象重复是否容易的多,所以指纹可以判定为Repuest是否重复的依据。

我们该如何判定呢?Scrapy是这样实现的

def__init__(self):
    self.fingerprints = set()

def repuest_seen(self,repuest;
tp = self.repuest_fingerprint(repuest)
if fp in self.fingerprints:
    return True
self.fingerprint.add(fp)

在去重的类RFPDupeFilter中,有一一个request_ seen 方法,该方法有一个参数request,它的作用就是检测Request对象是否重复。这个方法调用request_ fingerprint 获取该Request的指纹,检测这个指纹是否存在于fingerprints变量中,而fingerprints是一 个集合,集合的元素都是不重复的。如果指纹存在,就返回True,说明该Request是重复的,否则就将这个指纹加入集合。如果下次还有相同的Request 传递过来,指纹也是相同的,指纹就已经存在于集合中了,那么Request对象就会直接判定为重复。这样,去重的目的就实现了。

Scrapy的去重过程就是,利用集合元素的不重复特性来实现Request的去重。

对于分布式爬虫来说,我们肯定不能再用每个爬虫各自的集合来去重了。因为这样还是每个主机单独维护自己的集合,不能做到共享。多台主机如果生成了相同的Request, 只能各自去重, 各个主机之间就无法做到去重了。

那么要实现去重,这个指纹集合也需要是共享的。Redis 正好有集合的存储数据结构,我们可以利用Redis的集合作为指纹集合,那么这样去重集合也是利用Redis共享的。每台主机新生成Request后,把该Reques的指纹与集合比对,如果指纹已经存在,说明该Request是重复的,否则将Requet的指纹加人这个集合。利用同样的原理,我们在不同的存储结构中实现了分布式Reqeust的去重。


4.防止中断

在Scrapy中。爬虫运行时的Request队列放在内存中。爬虫运行中断后,这个队列的空间就被释放,此队列就销毁了。所以一且爬虫运行中断,爬虫再次运行就相当于全新的爬取过程。

要做到中断后继续爬取,我们可以将队列中的Request 保存起来,下次爬取直接读取保存数据即可获取上次爬取的队列。我们在Scrapy中指定一个爬取队列的存 储路径即可,这个路径使用J0B DIR变量来标识,可以用如下命令来实现:

scrapy crawl spider -S JOBDIR=crawls/spider

在Scrapy中,我们实际是把爬取队列保存到本地,第二次爬取直接读取并恢复队列。那么在分布式架构中,我们还用担心这个问题吗?不需要。因为爬取队列本身就是用数据库保存的,如果爬虫中断了,数据库中的Request依然存在,下次启动就会接着上次中断的地方继续爬取。

所以,当Redis的队列为空时,爬虫会重新爬取;当Redis的队列不为空时,爬虫便会接着上次中断之处继续爬取。

本期内容就到这里啦!打字不易,如果此文章对你有帮助的话,点个赞收个藏来个关注,给作者一个鼓励。也方便你下次能够快速查找!

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

clown空城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值