コード例 #1
0
ファイル: scheduler.py プロジェクト: Danny2024/scrapy-3.3.3
class Scheduler(object):
    def __init__(self, stats_collector):

        # 3.2.1-3: 调度器中接受统计器对象,使用统计器对象,对入对的请求数量和过滤掉的请求数量进行统计
        self.stats_collector = stats_collector

        # 准备队列,来缓存请求对象
        self.queue = Queue()
        # 2.0.2-5:统计总的请求数量
        # self.total_request_count = 0
        # 定义set集合,用于存储指纹数据
        # 2.修改init方法,创建去重容器
        self.filter_container = FilterContainer()

        # 定义变量,用于统计过滤掉多少请求
        # self.filter_request_count = 0
    def clear(self):
        """清除Redis中的指纹和请求数据"""
        if settings.SCHEDULE_PERSIST:
            # 清空redis队列中的数据
            self.queue.clear()
            # 清空指纹数据
            self.filter_container.clear()

    def add_request(self, request):
        # 3.2.1-4:使用stats_collector来统计总入对请求数量和过滤掉的请求数量
        # 3.3.1-2:只有需要过滤.并且重复了,才需要过滤
        if not request.dont_filter and self.seen_request(request):
            # 如果重复,就记录日志
            logger.info('过滤掉重复请求{}'.format(request.url))
            # self.filter_request_count += 1
            self.stats_collector.incr_filter_request_count()
            return

        # 添加请求对象
        self.queue.put(request)
        # print('endend..')
        # 2.0.2-5:每次添加请求的时候,就让total_request_count + 1
        # 此时的请求数量为入对的请求数量
        # self.total_request_count += 1
        self.stats_collector.incr_total_request_count()

    def get_request(self):
        # 获取请求对象
        return self.queue.get()

    def seen_request(self, request):
        # 用于爬虫请求是否已经被爬取过了
        # 获取请求获取请求对应的指纹
        fp = self._gen_fp(request)
        # 判断fp是否在容器中
        if self.filter_container.exists(fp):
            # 返回True,说明这个请求重复
            return True
        self.filter_container.add_fp(fp)
        # 返回False 说明这个请求不重复
        return False

    def _gen_fp(self, request):
        """
        根据请求对象生成指纹
        :param request: 请求对象
        :return: 请求对应的指纹
        思路:
            1.明确需要使用哪些数据生成指纹
                1.URL,方法名,params,data
            2.准备数据
            3.把数据添加到sha1算法中
            4.通过sha1获取16进制的指纹
        """
        # 2.准备数据
        # 对url进行规范化处理
        url = canonicalize_url(request.url)
        # 方法名
        method = request.method.upper()

        # GET的请求参数
        params = request.params if request.params else {}
        # 但是字典是无序的,我们把他转换成元祖再排序
        params = sorted(params.items(), key=lambda x: x[0])

        # POST请求的请求体数据
        data = request.data if request.data else {}
        # 但是字典是无序的,我们把他转换成元祖再排序
        data = sorted(data.items(), key=lambda x: x[0])

        # 获取sha1对象
        sha1 = hashlib.sha1()
        # 更新数据
        sha1.update(self.get_bytes_from_str(url))
        sha1.update(self.get_bytes_from_str(method))
        sha1.update(self.get_bytes_from_str(str(params)))
        sha1.update(self.get_bytes_from_str(str(data)))
        # 获取16进制的指纹数据
        return sha1.hexdigest()

    # py3中字符串默认是str,str就是一个unicode的字符串
    # py2中字符串str是二进制数据,解码后是unicode类型

    def get_bytes_from_str(self, s):
        if six.PY3:
            # 如果是py3,如果是字符串str就进行编码
            if isinstance(s, str):
                return s.encode('utf8')
            else:
                return s
        else:
            # py2,如果是str类型就直接返回,decode()之后变为unicode
            if isinstance(s, str):
                return s
            else:
                # 在py2中encode默认使用ascli码进行编码的,所以此处不能省
                return s.decode('utf8')
コード例 #2
0
ファイル: scheduler.py プロジェクト: 952700/scrapy-plus
class Scheduler(object):
    def __init__(self, stats_collector):
        #  3.2.1-3: 调度器中接收统计器对象, 使用统计器对象, 对入队请求数量和过滤掉请求数量进行统计
        self.stats_collector = stats_collector

        # 准备队列, 来缓存请求对象
        self.queue = Queue()
        #  2.0.2-5: 统计总的请求数量
        # self.total_request_count = 0
        # 定义set集合, 用于存储指纹数据
        # 2. 修改init方法, 创建去重容器
        self.filter_container = FilterContainer()
        # 定义变量, 用于统计过滤掉多少请求
        # self.filter_reqeust_count = 0

    def clear(self):
        """请求Redis中的指纹和请求数据"""
        if settings.SCHEDULE_PERSIST:
            # 清空Redis队列中数据
            self.queue.clear()
            # 请求空指纹数据
            self.filter_container.clear()

    def add_request(self, request):
        #  3.2.1-4: 使用stats_collector来通过入队请求数量和过滤掉请求数量
        #  3.3.1-2: 只有需要过滤 并且 已经重复, 才过滤
        if not request.dont_filter and self.seen_request(request):
            # 如果重复, 就记录日志
            logger.info('过滤掉重复请求:{}'.format(request.url))
            # self.filter_reqeust_count += 1
            self.stats_collector.incr_filter_request_count()
            return

        # print(request.url)
        # 添加请求对象
        self.queue.put(request)
        # print('添加请求:{}'.format(request.url))
        # 2.0.2-5: 每次添加请求的时候, 就让total_request_count增加1
        # 此处请求数量, 为入队请求数量
        # self.total_request_count += 1
        self.stats_collector.incr_total_request_count()

    def get_request(self):
        # print("获取请求")
        # 获取请求对象
        req = self.queue.get()
        # print("取出了:{}".format(req.url))
        return req

    def seen_request(self, request):
        # 用于爬虫请求是否已经爬取过了, 待实现
        # 根据请求获取该请求对应指纹
        fp = self._gen_fp(request)
        # 判断fp是否在容器中
        if self.filter_container.exists(fp):
            # 返回True,说明这个请求重复了
            return True

        # 如果不重复就来到这里, 把指纹添加过滤容器中
        self.filter_container.add_fp(fp)
        # 返回False, 就表示不重复
        return False

    def _gen_fp(self, request):
        """
        根据请求对象, 生成指纹
        :param request: 请求对象
        :return: 请求对应指纹
        思路:
         1. 明确需要使用那些数据生成指纹
            1. URL,方法名,params,data
         2. 准备数据
         3. 把数据添加到sha1算法中
         4. 通过sha1获取16进制的指纹
        """
        # 2. 准备数据
        # 对URL进行规范化处理
        url = canonicalize_url(request.url)
        # 方法名
        method = request.method.upper()
        # GET的请求参数
        params = request.params if request.params else {}
        # 但是字典是无序, 我们把转换为元祖在排序
        params = sorted(params.items(), key=lambda x: x[0])

        # POST请求的请求体数据
        data = request.data if request.data else {}
        # 但是字典是无序, 我们把转换为元祖在排序
        data = sorted(data.items(), key=lambda x: x[0])

        # 3. 获取sha1算法对象
        sha1 = hashlib.sha1()
        # 更新数据
        sha1.update(self.get_bytes_from_str(url))
        sha1.update(self.get_bytes_from_str(method))
        sha1.update(self.get_bytes_from_str(str(params)))
        sha1.update(self.get_bytes_from_str(str(data)))

        # 获取十六进制的指纹数据
        return sha1.hexdigest()

    def get_bytes_from_str(self, s):
        if six.PY3:
            # 如果是py3, 如果是str类型就需要进行编码
            if isinstance(s, str):
                return s.encode('utf8')
            else:
                return s
        else:
            # 如果是py2, 如果是str类型就直接返回
            if isinstance(s, str):
                return s
            else:
                # 在py2中encode默认使用ascii码进行编码的,此处不能省
                return s.encode('utf8')
コード例 #3
0
class Scheduler(object):
    def __init__(self, stats_collector):
        # 创建队列, 用于缓存请求对象
        self.queue = Queue()
        # 创建一个去重容器: 使用set集合
        self.filter_container = FilterContainer()
        # 接收引擎传递过来统计器对象
        # 用于统计总请求数量和重复的请求数量
        self.stats_collector = stats_collector

    def clear(self):
        # 如果是分布时候, 清空请求队列和指纹容器
        if settings.SCHEDULER_PERSIST:
            # 清空请求队列
            self.queue.clear()
            # 清空指纹容器
            self.filter_container.clear()

    def add_request(self, request):

        # 如果该请求需要被过滤 并且 重复了, 才过滤该请求
        if not request.dont_filter and self.filter_request(request):
            # 统计被过滤掉请求数量
            # 让统计器中repeat_request_nums_key对应值增加1
            self.stats_collector.incr(
                self.stats_collector.repeat_request_nums_key)

            # 如果请求已经存在, 就不在入队了
            return

        # 如果dont_filter为true就会直接入队
        # 如果来到这里, 说明该请求不重复.
        # 把请求对象, 添加到队列中
        self.queue.put(request)
        # 每次添加一个请求, 到队列中, 就让请求总数量增加1
        self.stats_collector.incr(self.stats_collector.request_nums_key)

    def get_request(self):
        # 从队列中, 取出请求对象, 并返回
        return self.queue.get()

    def filter_request(self, request):
        # 实现请求去重, 如果该请求需要过滤就返回True, 否则返回False
        # 1. 把请求对象生成一个指纹
        fp = self.__gen_fp(request)
        # 2. 如果指纹fp, 在去重容器中, 说明该请求已经存在了, 此时返回True
        if self.filter_container.exists(fp):
            logger.info("被过滤掉请求: {}".format(request.url))
            return True
        # 3. 如果代码能来到这里, 说明这个指纹在指纹容器中不存在
        # 把该指纹添加到指纹容器中
        self.filter_container.add_fp(fp)

        # 返回False, 说明该请求是一个全新请求
        return False

    def __gen_fp(self, request):
        """
        把请求对象生成一个指纹字符串
        :param request: 请求对象
        :return: 指纹字符串
        """
        # 1. 请求方法
        method = request.method.upper()
        # 2. 请求的URL, 对URL进行规范化处理
        url = canonicalize_url(request.url)
        # 3. 请求参数: params, 对字典参数进行排序
        params = sorted(request.params.items(), key=lambda x: x[0])
        # 4. 请求体
        data = sorted(request.data.items(), key=lambda x: x[0])

        # 创建sha1算法对象
        sha1 = hashlib.sha1()
        # 向sha1算法中添加需要生成指纹的数据
        # 把方法名添加到sha1算法中
        sha1.update(self.__str_to_bytes(method))
        # 把请求的URL, 添加到sha1算法中
        sha1.update(self.__str_to_bytes(url))
        # 把请求的params参数, 添加到sha1算法中
        sha1.update(self.__str_to_bytes(str(params)))
        # 把请求的data, 添加到sha1算法中
        sha1.update(self.__str_to_bytes(str(data)))

        # 使用sha1算法中数据, 生成一个指纹字符串
        return sha1.hexdigest()

    def __str_to_bytes(self, s):
        if six.PY3:
            # 如果是python3: str类型: Unicode的字符串, bytes类型: 二进制
            return s.encode('utf8') if isinstance(s, str) else s
        else:
            # 如果是python2: str类型: 是二进制数据, unicode类型: 是字符串;
            # python2: 默认编码方式 ASCII码, 所以此处必须指定
            return s if isinstance(s, str) else s.encode('utf8')
コード例 #4
0
class Scheduler(object):
    """调度器
    1. 缓存请求
    2. 请求去重
    """
    def __init__(self, stats_collector):
        # 接收传递过来的统计器对象
        self.stats_collector = stats_collector
        # 创建队列对象,用于缓存请求
        self.queue = Queue()
        # 创建指纹容器,用于存储指纹数据
        self.__filter_container = FilterContainer()

    def add_request(self, request):
        """添加请求到请求对列中"""
        # 如果需要过滤,并且是重复请求才过滤
        if not request.dont_filter and self.__filter_request(request):
            # 如果请求需要过滤,记录日志,直接返回
            logger.info('过滤掉了重复的请求:%s' % request.url)
            self.stats_collector.incr(
                self.stats_collector.repeat_request_nums_key)
            return

        self.queue.put(request)
        # 每添加一次请求,就让总请求数量加1
        self.stats_collector.incr(self.stats_collector.request_nums_key)

    def get_request(self):
        # 从队列中获取请求,并返回请求
        return self.queue.get()

    def __filter_request(self, request):
        """
        请求去重
        :return: 如果是True,说明这个请求重复了,需要过滤,否则不用过滤
        """
        # 1.获取请求对应指纹
        fp = self.__get_fp(request)
        # 如果指纹在容器里,说明这个请求已经重复了
        if self.__filter_container.exists(fp):
            return True

        # 能来到这里,说明这个请求是全新的,把指纹添加到指纹容器中
        self.__filter_container.add_fp(fp)
        return False

    def __get_fp(self, request):
        """
        生成请求对应指纹
        :param request: 请求对象
        :return: 指纹
        """
        # 创建sha1算法对象
        s = sha1()
        # 添加请求方法名到sha1算法中
        s.update(self.__to_bytes(request.method.upper()))
        # 添加请求URL名,到sha1算法中
        s.update(self.__to_bytes(canonicalize_url(request.url)))
        # 添加请求参数名到sha1算法中
        # 对请求参数进行排序
        params = sorted(request.params.items(), key=lambda x: x[0])
        s.update(self.__to_bytes(str(params)))
        # 添加请求体名导sha1算法中
        data = sorted(request.data.items(), key=lambda x: x[0])
        s.update(self.__to_bytes(str(data)))

        return s.hexdigest()

    @staticmethod
    def __to_bytes(i):
        """无论是py2还是py3,都把字符串转换为二进制"""
        if six.PY2:
            # py2中,str类型是二进制数据,unicode类型是字符串,默认使用ASCII编码
            return i if isinstance(i, str) else i.encode('utf8')
        else:
            # py3中,str类型是字符串,bytes是二进制类型
            return i.encode('utf8') if isinstance(i, str) else i

    def clear(self):
        """当分布式的时候,清空指纹和请求对列"""
        if settings.SCHEDULER_PERSIST:
            # 清空请求队列
            self.queue.clear()
            # 清空指纹数据
            self.__filter_container.clear()