redis 布隆与去重

布隆去重器

import redis
from hashlib import md5


class SimpleHash(object):
    def __init__(self, cap, seed):
        self.cap = cap
        self.seed = seed

    def hash(self, value):
        ret = 0
        for i in range(len(value)):
            ret += self.seed * ret + ord(value[i])
        return (self.cap - 1) & ret


class BloomFilter(object):
    def __init__(self, host='localhost', port=6379, db=4, blockNum=1, key='bloomfilter'):
        """
        :param host: the host of Redis
        :param port: the port of Redis
        :param db: witch db in Redis
        :param blockNum: one blockNum for about 90,000,000; if you have more strings for filtering, increase it.
        :param key: the key's name in Redis
        """
        self.server = redis.Redis(host=host, port=port, db=db)
        self.bit_size = 1 << 31  # Redis的String类型最大容量为512M,现使用256M
        self.seeds = [5, 7, 11, 13, 31, 37, 61]
        self.key = key
        self.blockNum = blockNum
        self.hashfunc = []
        for seed in self.seeds:
            self.hashfunc.append(SimpleHash(self.bit_size, seed))

    def isContains(self, str_input):
        if not str_input:
            return False
        m5 = md5()
        m5.update(str_input.encode('utf-8'))
        str_input = m5.hexdigest()
        ret = True
        name = self.key + str(int(str_input[0:2], 16) % self.blockNum)
        for f in self.hashfunc:
            loc = f.hash(str_input)
            ret = ret & self.server.getbit(name, loc)
        return ret

    def insert(self, str_input):
        m5 = md5()
        m5.update(str_input.encode('utf-8'))
        str_input = m5.hexdigest()
        name = self.key + str(int(str_input[0:2], 16) % self.blockNum)
        for f in self.hashfunc:
            loc = f.hash(str_input)
            self.server.setbit(name, loc, 1)

if  __name__=='__main__':
    """ 第一次运行时会显示 not exists!,之后再运行会显示 exists! """
    bf = BloomFilter()
    if bf.isContains('http://www.tencent.com'):  # 判断字符串是否存在
        print('exists!')
    else:
        print('not exists!')
        bf.insert('http://www.tencent.com')

python redis 布隆过滤器

class BloomFilter(object):
    """
    布隆过滤器客户端,具体的我就不实现了,可以百度。
    网络上有很多基于redis的实现,稍作修改就可以用于此场景,下面假设几个接口
    """
    def exist(self,fp,server,key):
    """
    fp: 请求指纹
    server: redis客户端连接
    key: bloomfilter对应的redis中的键名
    判断给定的指纹是否在布隆过滤器中。
    有一点需要注意,我们的传参相较于一般的bloomfilter多了server和key,因为我们使用的server和key是不固定的,经常变化。
    而一般的实现中server和key基本是不变的,直接作为类属性存储。
    如果存在,返回True,否则,返回False
    """
        pass
    def insert(self,fp,server,key):
    """
    参数含义和exist一致
    将给定请求指纹加入到指定布隆过滤器中
    """
        pass
class RFPDupeFilter(BaseDupeFilter):
    """
    大部分跟这个实现不相关的内容我就省略了,
    具体参考scrapy_redis的源码
    """
    """
    假设我们已经利用from_crawler中获取了相关数据:
    # bloomfilter粒度,'d'表示天,'m'表示月
    self.filter_granularity='d'
    # 去重周期,同时也是bloomfilter的数量
    self.filter_num=7
    并且初始化了bloomfliter
    self.bloomfilter=BloomFilter()
    """
    def get_bloomfilter_keys(self):
        """
        根据当前时间,产生一个bloomfilter key的列表
        假设当前时间为20181208,filter_granularity='d',filter_num=7,则返回['xxx:20181202','xxx:20181203',...,'xxx:20181208'],其中,'xxx'代表RFPDupeFilter的key,一般是 项目名:dupefilter 的形式
        假设当前时间为20181208,filter_granularity='m',filter_num=6,则返回['xxx:201807','xxx:201808',...,'xxx:201812']
        不难理解,就不实现了
        """
        pass
    def _exist(self,fp,key):
        """
        判断给定指纹是否存在于给定key对应的bloomfilter中
        """
        return self.bloomfilter.exsit(fp,self.server,key)
    def exist(self,fp):
        """
        判断给定指纹是否在bloomfilter中
        """
        # 为了应对日期变化,比如从12.08到12.09过度之类的,需要每次都重新计算当前有效的bloomfilter的键名列表
        bloomfilter_keys=self.get_bloomfilter_keys()
        for key in bloomfilter:
            # 任何一个存在就说明已经请求过,就可以退出了
            if self._exist(self,fp,key):
                return True
        # 全都不存在,返回false
        return False
    def insert(self,fp):
        """
        将给定指纹写入到布隆过滤器中
        """
        # 获取当天时间对应的bloomfilter键名
        key=self.get_bloomfilter_keys()[-1]
        # 写入
        self.bloomfilter.insert(fp,self.server,key)
    def request_seen(self, request):
        # 计算request的指纹
        fp = self.request_fingerprint(request)
        # 判断指纹是否已经存在
        if self.exist(fp):
            # 已存在
            return True
        # 不存在,加入到指纹集合中
        self.insert(fp)

原去重

def request_seen(self, request):
        """Returns True if request was already seen.
        Parameters
        ----------
        request : scrapy.http.Request
        Returns
        -------
        bool
        """
        fp = self.request_fingerprint(request)
        # This returns the number of values added, zero if already exists.
        added = self.server.sadd(self.key, fp)
        return added == 0

修改后

import time
# 过期时间,真正实现中应该写在settings部分,并通过`from_crawler`方法获取,这里为了说明方便,就随意一点了
# 过期时间设置成了30天
expire_time=60*60*24*30
def request_seen(self, request):
        # 计算request的指纹
        fp = self.request_fingerprint(request)
        # 尝试获取redis中该指纹的过期时间
        fp_expire_time=self.server.hget(self.key,fp)
        # 如果指纹在散列表中并且已经过期,或者不在散列表中
        if (fp_expire_time is None) or (int(fp_expire_time)<int(time.time())):
            self.server.hset(self.key,fp,int(time.time())+expire_time)
            return False
        else:
            return True
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容

  • SDS设计与实现: 一个带长度信息的字节数组[https://blog.csdn.net/qq_41854911/...
    JacquesWanna阅读 208评论 0 0
  • 人文党建“寻湖之记忆,绘党之风采”暑期社会实践团队红色漫画绘制 2022年6月29日,湖州学院人文学院党建中心“寻...
    海盐烤栗子阅读 68评论 0 0
  • 20220629 《问题即答案》 著名英国生物学家珍·古道尔说:“在我看来,提问能力是人类区别于动物的基本属性...
    sangrea1阅读 128评论 0 1
  • 2021-08-09: POJO类中布尔类型的变量,命名时不要加is,否则部分框架解析时会引起序列化错误。(因为在...
    windUtterance阅读 977评论 0 3
  • # 前言 ### 为什么我要尝试写作技术书籍 - 一个人年轻时经历的艰难会在未来成为他的财富 # 第一篇 基础和应...
    zhzosh阅读 580评论 0 0