[爬蟲進階]使用布隆過濾器去重


[爬蟲進階]使用布隆過濾器去重

原文鏈接:https://www.cnblogs.com/blog5277/p/9340637.html

原文作者:博客園--曲高終和寡

*******************如果你看到這一行,說明爬蟲在本人還沒有發布完成的時候就抓走了我的文章,導致內容不完整,請去上述的原文鏈接查看原文****************

 

寫爬蟲的人,一定會遇到很多問題

尤其是寫分布式,大規模爬蟲的時候,

這一條數據是否已經在數據庫里了?

是否已經在本機的內存里了?

是否已經在別的服務器的內存里了?

解決的辦法有很多,不同場景要用不同的解決方案.但是有一點,去重終歸是要拿這條數據和一個數據集作比較的.

那這個數據集越大,比較就越耗費資源(內存,時間),那有沒有什么業內通用的解決辦法呢?自然是有的,兩個:

bitmap和布隆過濾器

bitmap有一個缺點:它占用的空間會隨着這個數據集里面最大的空間變大而線性變大.(解決方案是可以hash一下,不過這樣的話又增加了錯誤率)

布隆過濾器的缺點就是:會有一定的錯誤率.

具體的研究請參考:

https://blog.csdn.net/zdxiq000/article/details/57626464

https://blog.csdn.net/tianyaleixiaowu/article/details/74721877

這兩篇文章,詳細的講解了bitmap和布隆過濾器的原理

 

其實知道了原理,絕大多數人也寫不出來代碼實現,寫出來了,也不一定能比大神寫的更好更優雅,說不定會有隱藏的BUG坑.那么有沒有現成的工具使用呢?自然是有的,Google公司出品的工具包里面就有.去搜索"guava maven",招待最新的版本,現在是這個:

        <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>25.1-jre</version>
        </dependency>

然后寫一個測試類:

    public static void main(String[] args) {
        try {
            int size = 1000000;
            BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size);
            Instant now0 = Instant.now();
            for (int i = 0; i < size; i++) {
                bloomFilter.put(i);
            }
            Instant now1 = Instant.now();
            System.out.println("錄入" + size + "條數據共耗時:" + (now1.toEpochMilli() - now0.toEpochMilli()));

            Instant now2 = Instant.now();
            for (int i = 0; i < size; i++) {
                if (!bloomFilter.mightContain(i)) {
                    System.out.println("有漏掉的");
                }
            }
            Instant now3 = Instant.now();
            System.out.println("判斷" + size + "條在過濾器中的數據共耗時:" + (now3.toEpochMilli() - now2.toEpochMilli()));

            Instant now4 = Instant.now();
            List<Integer> list = new ArrayList<Integer>(1000);
            for (int i = size + size / 10; i < size + (size * 2) / 10; i++) {
                if (bloomFilter.mightContain(i)) {
                    list.add(i);
                }
            }
            Instant now5 = Instant.now();
            System.out.println("判斷" + size / 10 + "條不在過濾器中的數據共耗時:" + (now5.toEpochMilli() - now4.toEpochMilli()));

            System.out.println("有誤判的數量:" + list.size());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

執行測試就知道了,速度非常快,占空間非常低.

不過記得,谷歌的方法默認誤判率是3%,你可以控制這個的大小,如下圖所示:

但是這樣的話,占用的空間就變大了.可以自己衡量.

(我是傾向於默認的3%的,因為這個誤判,是100%不會放過重復數據的,不過會把一些要抓的東西漏抓了而已.大規模的爬蟲,爬的數據何止千千萬,也不差這3%)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM