Python爬虫去重方案

Python爬虫去重方案

为什么去重?
  • 防止发出重复请求
  • 防止存储重复数据 (字符串、文件、图片)
去重实现基本原理

根据给定的判断依据和给定的去重容器,将原始数据逐一进行判断,判断去重容器中是否有该数据,若没有该数据则添加进去重容器中,同时标记该数据不是重复数据。 若去重容器中有该数据则不添加,同时标记该数据为重复数据

去重需求步骤
  1. 先明确数据类型(字符串、数值、对象…) 不同数据类型去重方案不一样

  2. 明确重复的界定,什么情况下是重复的,怎么判断的?

    # 剔除重复项   == 
    data = [123,123,121,'23','123','123',23212]
    set(data)
    # {'23', '123', 23212, 121, 123}
    
    # 剔除重复项   相同 
    # 先将列表中所有元素转为字符串再去重
    set([str(i) for i in mylist])
    #{'23', '123', '121', '23212'}
    
    mylist = [123,123,121,'23','123','123',23212]
    result = []   #去重结果
    sign = []     #判断依据  str(原始数据)   统一以字符串形式存放mylist
    for new_data in  mylist:
        if str(new_data) not in sign:      
            sign.append(str(new_data))    
            result.append(new_data)
           
    #[123, 121, '23', 23212]
    
信息摘要算法
  • 信息摘要 hash算法指可以将任意长度文本、字节数据,通过一个算法得到一个固定长度文本。如MD5(128位)、SHA1(160位)等。
    • 特征:只要源文本不同,计算得到的结果必然不同(摘要)
    • 摘要:摘要算法主要用于比对信息源是否一致,因为只要源发生变化,得到的摘要必然不同;而且摘要通常要比源短很多,所以称为摘要
  • 因此,利用摘要算法能大大降低去重容器的存储空间使用率,并提高判断速度,且由于其唯一性的特征,几乎不存误判
信息摘要hash算法去重方案实现
  • 普通内存版本
  • Redis持久化版本
  • Mysql持久化版本
import hashlib
import six


#基类
class BaseFilter(object):

    def __init__(self, hash_func_name="md5",mysql_url="None",mysql_table_name="filter",):
        self.mysql_url = mysql_url
        self.hash_func = getattr(hashlib, hash_func_name)
        self.storage = self._get_storage()

        self.mysql_table_name = mysql_table_name

    def _safe_data(self, data):
        '''
        加密编码时
        python2 中  str === python3 中的 bytes
        python2 中  unicocde === python3 中的 str
        :param data:给定原始数据
        :return:二进制类型字符串
        '''
        if six.PY3:
            if isinstance(data, str):
                return data.encode()
            elif isinstance(data, bytes):
                return data
            else:
                raise Exception("请提供二进制或字符串")
        else:
            if isinstance(data, unicode):
                return data.encode()
            elif isinstance(data, str):
                return data
            else:
                raise Exception("请提供str或unicode")

    def _get_hash_value(self, data):
        '''
        通过二进制格式原始数据生成对应指纹
        :param data: 二进制原始数据
        :return:hash值
        '''
        hashobj = self.hash_func()
        hashobj.update(self._safe_data(data))
        hash_value = hashobj.hexdigest()
        return hash_value

    def save(self, data):
        '''
        根据data计算出对应指纹进行存储
        :param data: 给定原始数据
        :return: 存储结果 True False
        '''
        hash_value = self._get_hash_value(data)
        return self._save(hash_value)

    def _save(self, hash_value):
        '''
        存储对应的hash值 (交给对应子类继承)
        :param hash_value:通过信息摘要算法求出hash值
        :return: 存储结果  True False
        '''
        pass

    def is_exits(self, data):
        '''
        通过判断给定数据对应的指纹是否存在
        :param data: 原始数据
        :return: 查询结果  True False
        '''
        hash_value = self._get_hash_value(data)
        return self._is_exits(hash_value)

    def _is_exits(self, hash_value):
        '''
        判断对应的hash是否存在(交给对应子类继承)
        :param hash_value:
        :return: True False
        '''
        pass

    def _get_storage(self):
        '''
        返回对应存储对象
        :return:
        '''
        pass


from . import BaseFilter


#基于内存去重方案

class MemoryFilter(BaseFilter):

    def _save(self, hash_value):
        '''
        利用set进行存储
        :param hash_value:
        :return:
        '''
        return self.storage.add(hash_value)

    def _is_exits(self, hash_value):
        if hash_value in self.storage:
            return True
        return False

    def _get_storage(self):
        return set()



from . import BaseFilter
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

#基于mysql去重方案

class Filter(Base):
    __tablename__ = "filter"
    id = Column(Integer, primary_key=True)
    hash_value = Column(String(40), index=True, unique=True)


# mysql_url = "pymysql+mysql://root:password@localhost:3306/db_name?charset=utf8"

class MysqlFilter(BaseFilter):


    def _get_storage(self):
        engine = create_engine(self.mysql_url)
        Base.metadata.create_all(engine)  # 创建表
        session = sessionmaker(engine)
        return session

    def _save(self, hash_value):
        session = self.storage()
        filter = Filter(hash_value=hash_value)
        session.add(filter)
        session.commit()
        session.close()

    def _is_exits(self, hash_value):
        session = self.storage()
        ret = session.query(Filter).filter_by(hash_value = hash_value).first()
        session.close()

        if ret is None:
            return False
        return True

# 基于Reids的持久化存储去重判断依据
import redis
from . import BaseFilter


class RedisFilter(BaseFilter):

    def __init__(self, redis_host="localhost", redis_port=6379, redis_db=0, redis_key="filter"):
        self.redis_host = redis_host
        self.redis_port = redis_port
        self.redis_db = redis_db
        self.redis_key = redis_key

    def _get_storage(self):
        '''
        返回 redis 连接池连接对象
        :return:
        '''
        pool = redis.ConnectionPool(host=self.redis_host, port=self.redis_port, db=self.redis_db)
        client = redis.StrictRedis(connection_pool=pool)
        return client

    def _save(self, hash_value):
        '''
        将数据存储到redis的无序集合中
        :param hash_value:
        :return:
        '''
        return self.storage.sadd(self.redis_key, hash_value)

    def _is_exits(self, hash_value):
        '''
        判断redis无序集合中是否有对应判断数据
        :param hash_value:
        :return:
        '''
        return self.storage.sismember(self.redis_key, hash_value)

Simhash 算法

Simhash算法是一种局部敏感哈希算法,能实现相似文本内容的去重。

两篇文章得出的Simhash值

Simhash对长文本500字+比较适用,短文本可能偏差较大

在Google的论文给出数据中,64位的Simhash值,在海明距离(两个Simhash值二进制不对应的个数)为3的情况下,可以认为两篇文档是相似或重复的(仅参考)

请添加图片描述
请添加图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值