爬虫进阶:框架功能完善-多爬虫实现之去重

未匹配的标注

去重原理

去重的理解

其实就只是对以往数据进行一个比对,判断是否已经存在

可大致分为:对原始数据比对、对利用原始数据生成的特征值进行比对两种方式

原始数据比对很好理解,就是比对的时候参照值就是原始数据;而利用特征值比对,比如最典型的就是利用原始数据生成一个指纹,比对的参照值就是这个指纹,不是原始数据本身,主要应用于单个原始数据比较大的情况,另外一种常用就是布隆过滤器,这种方式原始利用一种”特征值”,应用场景是海量数据的去重(但具有一定几率的误判)。

爬虫请求去重原理和实现

根据请求的url、请求方法、请求参数、请求体进行唯一标识,进行比对,由于这四个数据加到一起,内容较长,因此使用求指纹的方式来进行去重判断。

指纹计算方法,最常用的就是md5、sha1等hash加密算法,来求指纹

具体实现如下:

# scrapy_plus/core/scheduler.py
# 利用six模块实现py2和py3兼容
# coding=utf-8
# from six.moves.queue import Queue
from scrapy_plus.utils.log import logger
import w3lib.url
from hashlib import sha1
import six
from scrapy_plus.utils.queue import Queue
from scrapy_plus.utils.set import RedisFilterContainer,NoramlFilterContainer
from scrapy_plus.conf.settings import SCHEDULER_PERSIST


class Scheduler:

    def __init__(self,collector):
        self.queue = Queue()
        self._filter_container = set()
        self.repeate_request_num = 0 #统计重复的数量

    def add_request(self, request):
        if self._filter_request(request):
            self.queue.put(request)

    def get_request(self):
        try:
            return self.queue.get(False)
        except:
            return None

    def _filter_request(self, request):
        # 去重容器:存储已经发过的请求的特征 url    选用集合类型:set()
        # 利用请求的url method data  求出一个指纹  利用sha1
        request.fp = self._gen_fp(request)
        if request.fp not in self._filter_container:
            self._filter_container.add_fp(request.fp)
            # logger.info("添加新的请求:<%s>" % request.url)
            return True
        else:
            logger.info("发现重复的请求:<%s>" % request.url)
            self.repeate_request_num +=1
            return False

    def _gen_fp(self, request):
        '''请求去重,计算指纹'''
        # 用来判断请求是否重复的属性:url,method,params,data
        # 为保持唯一性,需要对他们按照同样的排序规则进行排序
        # 1. url排序:借助w3lib.url模块中的canonicalize_url方法
        url = w3lib.url.canonicalize_url(request.url)
        # 2. method不需要排序,只要保持大小写一致就可以
        method = request.method.upper()
        # 4. data排序:如果有提供则是一个字典,如果没有则是空字典
        data = request.data if request.data is not None else {}
        data = sorted(data.items(), key=lambda x: x[0])

        # 5. 利用sha1算法,计算指纹
        s1 = sha1()
        # 为了兼容py2和py3,利用_to_bytes方法,把所有的字符串转化为字节类型
        s1.update(self._to_bytes(url))
        s1.update(self._to_bytes(method))
        s1.update(self._to_bytes(str(data)))

        fp = s1.hexdigest()
        return fp

    @staticmethod
    def _to_bytes(string):
        if six.PY2:
            if isinstance(string, str):
                return string
            else:  #如果是python2的unicode类型,转化为字节类型
                return string.encode("utf-8")
        elif six.PY3:
            if isinstance(string, str): #如果是python3的str类型,转化为字节类型
                return string.encode("utf-8")
            else:
                return string

修改engine模块

现在统计了总的重复数量,所以,在engine中阻塞的位置判断程序结束的条件:成功的响应数+重复的数量>=总的请求数量程序结束

#scrapy_plus/core/engine.py
.....
def _start_engine(self):
        '''
        具体的实现引擎的细节
        :return:
        '''
        self._start_request()
        while True:
            time.sleep(0.001)
            self._execute_request_response_item()
            #成功的响应数+重复的数量>=总的请求数量 程序结束
            if self.total_response_nums+self.scheduler.repeat_request_number >= self.total_request_nums:
                break
.....

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 查看所有版本


暂无话题~