[爬虫进阶]使用布隆过滤器去重

[爬虫进阶]使用布隆过滤器去重

原文链接: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%)

原文地址:https://www.cnblogs.com/blog5277/p/9340637.html