Python爬虫中的去重处理
一:Python爬虫去重应用场景及基本原理
爬虫中什么业务需要使用去重?
- 防止发出重复的请求
- 防止存储重复的数据
去重实现的基本原理:
根据给定的判断依据和给定的去重容器,将原始数据逐一进行判断,判断去重容器中是否有该数据。如果没有那就把该数据对应的判断依据添加去重容器中,同时标记该数据是不重复数据;如果有就不添加,同时标记该数据是重复数据。
判断依据(原始数据,原始数据特征值)
去重容器(存储判断数据)
二:基于信息摘要算法的去重
信息摘要hash算法指可以将任意长度的文件,字节数据,通过一个算法得到一个固定长度的文件。如MD5(128位),SHA1(160位)等。hash算法得出的结果其实质上就是一串数值,如md5的128位指的是二进制长度,十六进制的长度是32位。一个十六进制等于四个二进制。
特征:只要源文本不同,计算得到的结果必然不同(摘要)
摘要:摘要算法主要用于比对信息源是否一致,因为只要源发生变化,得到的摘要必然不同;而且通常结果要比源短很多,所以称为“摘要”。
因此,利用信息摘要算法能大大降低去重容器的存储空间使用率,并提高判断速度,且由于其强唯一的特征,几乎不存在误判。
from hashlib import md5
m5 = md5()
s = 'life is short, you need python'
m5.update(s.encode()) # 必须传入二进制数据
print(m5.hexdigest())
# 0c653a0b179966f1f99b3a1f38a3f02e
2.1 信息摘要hash算法去重方案实现
实现以下三种方案:
- 普通内存版本
- Redis持久化版本
- MySQL持久化版本
python docker环境的部署与配置见:利用docker搭建Python开发环境
python docker环境的Dockerfile文件配置如下:
# 配置基本的python spider开发环境
FROM python:alpine3.7
# 修改alpine系统源及pip安装源
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN mkdir -p /root/.pip
RUN echo -e "[global]\nindex-url = http://mirrors.aliyun.com/pypi/simple/\n[install]\ntrusted-host = mirrors.aliyun.com" > /root/.pip/pip.conf
# 安装开发环境所需要的包
RUN apk add --no-cache build-base &&\
apk add --no-cache libffi-dev &&\
pip install ipython &&\
pip install redis &&\
pip install pymysql &&\
pip install sqlalchemy &&\
pip install six &&\
pip install w3lib &&\
apk del build-base
2.2 去重过滤器基类实现
# __init__.py
# 基于信息摘要算法进行数据的去重判断和存储
import hashlib
class BaseFilter(object):
'''基于信息摘要算法进行数据的去重判断和存储'''
def __init__(self, hash_func_nme = 'md5', redis_host = "localhost", redis_port = 6379, redis_password = '', redis_db = 0, redis_key = 'filter', mysql_url = None, mysql_table_name = 'filter'):
self.redis_host = redis_host
self.redis_port = redis_port
self.redis_db = redis_db
self.redis_password = redis_password
self.redis_key = redis_key
self.mysql_url = mysql_url
self.mysql_table_naem = mysql_table_name
self.hash_func = getattr(hashlib, hash_func_nme)
self.storage = self._get_storage()
def _get_hash_value(self, data):
'''
根据给定的数据,返回对应的信息摘要hash值
:param data: 给定的原始数据(二进制类型的字符串数据)
:return: hash值
'''
hash_obj = self.hash_func()
hash_obj.update(self._safe_data(data))
hash_value = hash_obj.hexdigest()
return hash_value
def _safe_data(self, data):
'''
:param data: 给定的原始数据
:return: 二进制类型的字符串数据
'''
if isinstance(data, bytes):
return data
elif isinstance(data, str):
return data.encode()
else: