前言
如果使用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,选择默认的安装就行。
原文始发于微信公众号(Python成长路):基于文件和内存的布隆过滤器
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/196198.html