在爬虫中,我们经常遇到这样的问题。一是希望抓取过的URL不再重复抓取,节省资源;二是希望下载过的数据不再重复下载(一般情况下保证了第一条可以差不多满足第二条,然而还是有一些情况同一资源会出现在不同的页面上)。这时候,其实你需要的,就是一个好的去重策略。今天我讲一下我在实战过程中使用的去重方案,供大家学习交流。
还有一个场景大家也应该考虑到,就是当下载量特别大的时候,往往程序需要执行很长时间。这时候一旦出现程序报错或者网络中断以及别的问题,导致爬虫停止运行,我们肯定是希望它能在当前停下的地方重新跑起来,也就是断点续“爬”。其实仔细想一下,这个问题可以用今天的去重策略来解决。当然,这个问题的解决方案并不只依赖于去重策略,还要依赖数据储存的一致性等复杂问题,我们后续再去讨论。
在开始今天的正文之前,先给大家补充点知识,什么是hash。hash中文译为散列,或者哈希。它是一个可以定义的需要满足一定条件的函数,这个函数的输入可以是任何数据,输出为一串并不很长的数字。同一散列函数的两个结果值如果不同,那他们的输入一定是不同的、基于哈希的这个确定性,我们可以轻松实现大量不确定是否一致的文件的一致性对比。哈希的这种特性也使得某些特定的哈希函数可以用来加密,比如常见的md5等。还有网站上下载大文件时,为了防篡改以及数据一致性,通常还会提供SHA-1值,这些由都是哈希函数生成的。关于哈希的介绍就先到这,咱们进入今天去重的正题。
下面我会从程序、数据库等几个层面详细叙述一下相应的去重策略。
程序层面,像十万以下URL的抓取可以简单的用set来实现去重。如果是百万或者千万量级的话,考虑到性能,我们应该使用基于hash的set实现去重。知道了前面哈希的知识,你肯定就懂这是为什么了。哈希使得我们并不需要对比超长的URL以及params,只需要对比其哈希值。但是仔细想一下,会存在这样一个问题,就是如果set中存在这个哈希值,并不能说明此URL被抓取过。仔细想想为什么呢?留给读者思考。但是能保证的是,如果哈希值不在set内,那此URL肯定没有被抓取过,这样就达成了我们的目的:在千万URL量级下,我们并不需要保证每一个URL都被访问仅一次,而只需要保证绝大多数URL没有被重复访问就OK了。
可能还有小伙伴会问,如果量级再大呢?其实像笔者,就做过上亿量级URL的抓取,这时候基于hash的set策略可能效果也并不理想。所以我们需要更优的策略。于是,Bloom Filter(中文布隆过滤器)算法成了我们新的选择。布隆过滤器呢,原理是这样的,一个URL过来,通过M个特别的哈希函数对其进行运算,映射成一个M维位数组的M个维度。新的URL诞生时,进行同样操作并逐个与set中的位数组做“与”运算,若结果改变则说明URL一定没有被抓取过,若结果一致则说明URL有一定概率被抓取过。布隆过滤器的插入和查询效率都是O(M),远低于其他一般策略。
以上是程序层面我们能做的处理,下面介绍一下数据库层面去重我们可以做的事。举两个例子吧,第一像MySQL这种关系型数据库,我们可以设置主键或者唯一索引来保证数据的唯一性。第二像MongoDB这种Schema free的非关系型数据库,我们可以用先检索再插入或者是upsert的方式保证数据的唯一性。
另外,基于内存的Redis数据库其高速的读写以及Redis hash特性也使得它能胜任去重数据库的需求。
好了,今天关于去重策略的讨论就到此结束了,大家有想要和我交流的可以关注本公众号并在下方菜单栏中获取我的联系方式。
另外欢迎大家加入我的知识星球[数据]和Python数据博客 https://pydata.me。星球内现有淘宝模拟登录讲解视频以及豆瓣模拟登录和自动顶帖讲解视频。
博客中现有淘宝模拟登录代码实现、豆瓣模拟登录代码和自动顶帖代码实现以及Cookie的相关知识讲解。
领取专属 10元无门槛券
私享最新 技术干货