在爬虫系统中,在内存中维护着两个关于URL的队列,ToDo队列和Visited队列,ToDo队列存放的是爬虫从已经爬取的网页中解析出来的即将爬取的URL,可是网页是互联的,极可能解析出来的URL是已经爬取到的,所以须要VIsited队列来存放已经爬取过的URL。当爬虫从ToDo队列中取出一个URL的时候,先和Visited队列中的URL进行对比,确认此URL没有被爬取后就能够下载分析来。不然舍弃此URL,从Todo队列取出下一个URL继续工做。java
而后,咱们知道爬虫在爬取网页时,网页的量是比较大的,直接将全部的URL直接放入Visited队列是很浪费空间的。所以引入bloom filter!数组
(关于使用bloomfilter的缘由:dom
visited队列中url过多,消耗内存空间是一方面。还有一个重要的缘由,在从todo队列中取出一个新的URL时,必须和 visited中全部URL比较,确保没有处理过。那么若是直接比较的话,是要比较N(visited中全部url个数)次的,并且这个N至关大,效率明 显不够。采用bloom filter,最多只要比较K(我在文章中写的,相互独立的散列函数的个数)次,由于只要一个散列函数的散列值对应的位是0,就能够肯定这个URL没有处 理过。ide
)函数
咱们把bloom filer设置为m个bit,所有初始为0。post
对每个URL,进行K(K<m)次相互独立的哈希,一共获得K个值,将这K个值在bloom filter中对应的bit位置1。url
通过上述处理的bloom filter实际上构成了咱们所说的Visited队列,当咱们从ToDo队列中取出一个新的URL时,一样,进行相同的K次哈希,每进行一次哈希,查看bloom filter中对应位,只要发现某位是0,就能够肯定这个URL是没有处理过的,能够继续下载处理。spa
那么,原理清楚以后,还有几个问题没有解决。.net
一、bloom filter是有可能发生错误的,由于不处理碰撞,也就是说,有可能把不属于这个集合的元素误认为属于这个集合code
错误率的计算:
在n个URL都进行k次散列加入以后,bloomfilter中某位是0的几率
错误率(即一个新的URL刚好k次散列的值对应的位都已是1的几率)
二、哈希函数个数K的肯定
k = ln2· (m/n)时(具体数学分析见http://blog.csdn.net/jiaomeng/article/details/1495500)
三、bloomfilter位数M的肯定
咱们能够想到,M的大小越大,错误率就会越小,可是数学证实给出了一个下界。即M = log2 e N = 1.44N。
附上java代码
1 /**屈永泉 布隆过滤器 快速肯定哪些网页已经被下载过*/ 2 3 package crawler; 4 5 import java.util.BitSet; 6 7 public class BloomFilter { 8 private int defaultSize = 5000 << 10000; 9 private int basic = defaultSize - 1; 10 private BitSet bits = new BitSet(defaultSize); 11 12 private int[] lrandom(String key) { // 产生八个随机数并返回 13 int[] randomsum = new int[8]; 14 for (int i = 0; i < 8; i++) 15 randomsum[0] = hashCode(key, i + 1); 16 return randomsum; 17 } 18 19 // 将一个URL加入 20 public synchronized void add(String key) { 21 int keyCode[] = lrandom(key); 22 for (int i = 0; i < 8; i++) 23 bits.set(keyCode[i]); // 将指定索引处的位设置为 true 24 } 25 } 26 27 // 判断一个URL是否存在 28 public boolean exist(String key) { 29 int keyCode[] = lrandom(key); 30 if (bits.get(keyCode[0]) 31 && bits.get(keyCode[1]) // 返回指定索引处的位值。 32 && bits.get(keyCode[2]) && bits.get(keyCode[3]) 33 && bits.get(keyCode[4]) && bits.get(keyCode[5]) 34 && bits.get(keyCode[6]) && bits.get(keyCode[7])) { 35 return true; 36 } 37 return false; 38 } 39 40 41 private int hashCode(String key, int Q) { 42 int h = 0; 43 int off = 0; 44 char val[] = key.toCharArray(); // 将此URl转换为一个新的字符数组 45 int len = key.length(); 46 for (int i = 0; i < len; i++) { 47 h = (30 + Q) * h + val[off++]; 48 } 49 return basic & h; 50 } 51 52 53 /* public static void main(String[] args) { // TODO Auto-generated method 54 long pre = 0; 55 long post = 0; 56 pre = System.nanoTime(); 57 BloomFilter f = new BloomFilter(); //初始化 58 f.add("http://www.agrilink.cn/"); f.add("http://www.baidu.com/"); 59 System.out.println(f.exist("http://www.baidu.com/")); 60 System.out.println(f.exist("http://www.baidud.com/")); 61 post = System.nanoTime(); 62 System.out.println("Time: " + (post - pre)); 63 64 } 65 */ 66 67 }