基于文件和内存的布隆过滤器

前言

如果使用scrapy的话,去重可以直接使用scrapy_redis和scrapy_redis_bloomfilter都是已经封装好了,直接修改配置就能使用,但是很多时候我只是写一个小爬虫,不想使用scrapy如何持久化去重。

基于redis的布隆过滤器

将需要去重的数据存成文件,或者存到redis集合也可以达到去重的效果。但是我又想使用布隆过滤器节省内存怎么办,那么可以直接使用scrapy_redis_bloomfilter这个库就行。

可以看到其实这个库里面真正去重的是bloomfilter.py这个文件,其他都是为了契合scrapy的代码,内容如下:

from .defaults import BLOOMFILTER_BIT, BLOOMFILTER_HASH_NUMBER


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


class BloomFilter(object):
    def __init__(self, server, key, bit=BLOOMFILTER_BIT, hash_number=BLOOMFILTER_HASH_NUMBER):
        # default to 1 << 30 = 10,7374,1824 = 2^30 = 128MB, max filter 2^30/hash_number = 1,7895,6970 fingerprints
        self.m = 1 << bit
        self.seeds = range(hash_number)
        self.server = server
        self.key = key
        self.maps = [HashMap(self.m, seed) for seed in self.seeds]
    
    def exists(self, value):
        if not value:
            return False
        exist = True
        for map in self.maps:
            offset = map.hash(value)
            exist = exist & self.server.getbit(self.key, offset)
        return exist
    
    def insert(self, value):
        for f in self.maps:
            offset = f.hash(value)
            self.server.setbit(self.key, offset, 1)

其中的server参数是redis.Redis对象,直接复制这个文件,修改默认配置,就可以使用了。

基于文件的布隆过滤器

我写了一个小爬虫,总共代码不过百行,不想使用redis来去重,可能服务器上没有或者单纯的我不想用redis怎么办。

其实布隆过滤器已经做好了,只是代码将去重的bit数组保存在redis而已,我们只需要修改保存位置,让它保存为文件即可。修改好的代码如下:

import os
import math
from bitarray import bitarray

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


class BloomFilter(object):
    def __init__(self, filename, count=10000, error_rate=0.001, remake=False):
        '''
        filename: 去重文件名
        count: 去重数据量
        error_rate: 容错率
        remake: 是否覆盖已存在文件,重新创建
        '''
        self.m = math.ceil(- (count * (math.log(error_rate, math.e)) / (math.log(2, math.e))**2))
        k = math.ceil(self.m / count * math.log(2, math.e))
        self.filename = filename
        if not os.path.exists(filename) or remake:
            self.bit_array = bitarray(self.m)
            self.bit_array.setall(0)
        else:
            self.bit_array = self.fromfile(filename)
        self.maps = [HashMap(self.m, seed) for seed in range(k)]
    
    def exists(self, value):
        if not value:
            return False
        exist = True
        for map in self.maps:
            offset = map.hash(value)
            exist = exist & self.bit_array[offset]
        return exist
    
    def insert(self, value):
        for f in self.maps:
            offset = f.hash(value)
            self.bit_array[offset] = 1
    
    def fromfile(self, filename):
        bit_array = bitarray()
        with open(self.filename, 'rb') as f:
            bit_array.fromfile(f)
        return bit_array

    def savefile(self):
        with open(self.filename, 'wb') as f:
            self.bit_array.tofile(f)

if __name__ == "__main__":
    bf = BloomFilter("bf.bin")
    n = 100
    for i in range(4000, 4000+ n, 3):
        bf.insert(str(i))
    for i in range(4000, 4000+ n):
        print(i,bf.exists(str(i)))
    bf.savefile()

说是基于文件其实不太准确,因为代码直接是加载整个文件到内存来去重的,当不使用的时候,才调用savefile方法将数组持久化保存到文件,所以说是基于内存更合适。代码很简单,只是将原来的server改成了bitarray类型的数组。

不过在Windows安装bitarray库是需要vc++14的,真就是个库就要这个环境。vc++14安装程序:https://wwx.lanzoux.com/iGwSNicm90b,选择默认的安装就行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值