爬虫进阶:框架功能完善-380玩彩网官网入口
去重原理
去重的理解
其实就只是对以往数据进行一个比对,判断是否已经存在
可大致分为:对原始数据比对、对利用原始数据生成的特征值进行比对两种方式
原始数据比对很好理解,就是比对的时候参照值就是原始数据;而利用特征值比对,比如最典型的就是利用原始数据生成一个指纹,比对的参照值就是这个指纹,不是原始数据本身,主要应用于单个原始数据比较大的情况,另外一种常用就是布隆过滤器,这种方式原始利用一种”特征值”,应用场景是海量数据的去重(但具有一定几率的误判)。
爬虫请求去重原理和实现
根据请求的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_numsself.scheduler.repeat_request_number >= self.total_request_nums:
break
.....