爬蟲技術之——bloom filter(含java代碼)

  在爬蟲系統中,在內存中維護着兩個關於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 }
View Code
相關文章
相關標籤/搜索