同一个网页链接被包含在多个页面中时,会导致爬虫在爬取的过程中,重复爬取相同的网页,如何避免这些重复的爬取呢?
注意点:
满足上述需求的数据结构有:
在支持快速查询和插入的同时需要关注内存的消耗。
假设有10亿网页,一条URL平均长度64byte,全部存储起来需要大约60GB的内存。散列表需要保证较低的装填因子,采用链表法则还需要保存指针,实际使用的存储空间可能需要超过100GB。大型搜索引擎会使用分治的思想将这100GB的数据保存在多台服务器上。
分治+散列表的思路可以实现,那么在添加、查询和内存消耗上是否还有优化空间?
注意:时间复杂度只是表示执行时间随数据规模的变化趋势,并不能度量在特定的数据规模下,代码执行时间的多少。在数据量很大的情况下,常数、系数和低阶的优化都能带来非常可观的收益。
分治+散列表思路中耗时的点在于:通过哈希函数定位到某个链表之后,还要依次比对每个链表中的 URL。
在内存消耗方面的优化,可以将散列表更换为布隆过滤器。布隆过滤器基于位图,且对位图进行了改进。
如何在数据范围是1~1亿之间的1千万个整数中快速查找某个整数是否存在。
散列表可以解决,位图也可以解决。
使用位图来解决。
array[5]=true
)array[K]
取出来判断是否为true注意:很多编程语言中提供的布尔类型,大小是 1 byte,并不能节省太多内存空间。实际上,表示 true 和 false 两个值,只需要用一个二进制位(bit)就可以。
如何通过编程语言,来表示一个二进制位(bit)可以使用位运算。
借助编程语言中提供的数据类型,比如 int、long、char 等类型,通过位运算,用其中的某个位表示某个数字。如下示例:
// byte占1个字节,8bit
type BitMap struct {
bytes []byte
nbits int
}
func NewBitMap(bits int) *BitMap {
return &BitMap{
bytes: make([]byte, bits/8+1),
nbits: bits,
}
}
func (b *BitMap) set(k int) {
if k > b.nbits {
return
}
byteIndex := k / 8
bitIndex := k % 8
b.bytes[byteIndex] |= 1 << bitIndex
}
func (b *BitMap) get(k int) bool {
if k > b.nbits {
return false
}
byteIndex := k / 8
bitIndex := k % 8
return (b.bytes[byteIndex] & (1 << bitIndex)) != 0
}
数据范围是1~1亿之间的1千万数据:
如果数据的范围更大,是1~10亿,数据量还是1千万,那么位图存储的空间是125MB,这就远大于散列表需要的存储空间。
这是就需要布隆过滤器来解决数据范围非常大这个问题了。
数据范围是1~10亿之间的1千万数据,使用布隆过滤器的处理策略是:
f(x)=x%n
,n表示位图大小,即对数字跟位图的大小进行取模求余),对数字进行处理,让它落在这 1 到 1 亿范围内注意:哈希会存在冲突,如一亿零一和 1 两个数字最终都存储在1这个位置。为了降低这种冲突概率,可以设计一个复杂点、随机点的哈希函数。
布隆过滤器的处理方法是:一个哈希函数可能会存在冲突,用多个哈希函数一块儿定位一个数据,来降低冲突的概率。
X1,X2,X3,…,XK
BitMap[X1]
,BitMap[X2]
,BitMap[X3]
,…,BitMap[XK]
都设置成 true,用 K 个二进制位,来表示一个数字的存在Y1,Y2,Y3,…,YK
对于两个不同的数字来说,经过一个哈希函数处理,可能会产生相同的哈希值。但是经过 K 个哈希函数处理之后,K 个哈希值都相同的概率就非常低了。
尽管采用 K 个哈希函数之后,两个数字哈希冲突的概率降低了,但是,这种处理方式又带来了新的问题,那就是容易误判。
bloom filter: False is always false. True is maybe true.
布隆过滤器只会对存在有误判:
只要调整哈希函数的个数、位图大小跟要存储数字的个数之间的比例,就可以将这种误判的概率降到非常低。
布隆过滤器的误判率,主要跟哈希函数的个数、位图的大小有关。
一般不用来删除,如果非要支持删除,可以再弄个数据结构记录删除的数据。
尽管布隆过滤器会存在误判,但是,这并不影响它发挥大作用。很多场景对误判有一定的容忍度。
比如要解决的爬虫判重这个问题,即便一个没有被爬取过的网页,被误判为已经被爬取,对于搜索引擎来说,也并不是什么大事情,是可以容忍的,毕竟网页太多了,搜索引擎也不可能 100% 都爬取到。
布隆过滤器非常适合这种不需要 100% 准确的、允许存在小概率误判的大规模判重场景。
比如,统计一个大型网站的每天的 UV(Unique Visitor,独立访客) 数,就可以使用布隆过滤器,对重复访问的用户进行去重。
使用布隆过滤器解决爬虫去重问题: