网络爬虫:URL去重策略之布隆过滤器(BloomFilter)的使用
前言:
? 最近被網(wǎng)絡(luò)爬蟲中的去重策略所困擾。使用一些其他的“理想”的去重策略,不過(guò)在運(yùn)行過(guò)程中總是會(huì)不太聽話。不過(guò)當(dāng)我發(fā)現(xiàn)了BloomFilter這個(gè)東西的時(shí)候,的確,這里是我目前找到的最靠譜的一種方法。
? 如果,你說(shuō)URL去重嘛,有什么難的。那么你可以看完下面的一些問(wèn)題再說(shuō)這句話。
關(guān)于BloomFilter:
? Bloom filter 是由 Howard Bloom 在 1970 年提出的二進(jìn)制向量數(shù)據(jù)結(jié)構(gòu),它具有很好的空間和時(shí)間效率,被用來(lái)檢測(cè)一個(gè)元素是不是集合中的一個(gè)成員。如果檢測(cè)結(jié)果為是,該元素不一定在集合中;但如果檢測(cè)結(jié)果為否,該元素一定不在集合中。因此Bloom filter具有100%的召回率。這樣每個(gè)檢測(cè)請(qǐng)求返回有“在集合內(nèi)(可能錯(cuò)誤)”和“不在集合內(nèi)(絕對(duì)不在集合內(nèi))”兩種情況,可見 Bloom filter 是犧牲了正確率以節(jié)省空間。
以前的去重策略:
1.想到過(guò)的URL去重策略
- 在數(shù)據(jù)庫(kù)中創(chuàng)建字段的UNIQUE屬性
- 在數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)唯一的索引,在插入數(shù)據(jù)之前檢查待插入的數(shù)據(jù)是否存在
- 使用Set或HashSet保存數(shù)據(jù),確保唯一
- 使用Map或是一個(gè)定長(zhǎng)數(shù)組記錄某一個(gè)URL是否被訪問(wèn)過(guò)
2.以上去重策略存在的問(wèn)題
? (1)對(duì)于在數(shù)據(jù)庫(kù)中創(chuàng)建字段的UNIQUE屬性, 的確是可以避免一些重復(fù)性操作。不過(guò)在多次MySQL報(bào)錯(cuò)之后,程序可能會(huì)直接崩潰,因此這種方式不可取
? (2)如果我們要在每一次插入數(shù)據(jù)之前都去檢查待插入的數(shù)據(jù)是否存在,這樣勢(shì)必會(huì)影響程序的效率
? (3)這種方式是我在第一次嘗試的時(shí)候使用的,放棄繼續(xù)使用的原因是:OOM。當(dāng)然,這里并不是程序的內(nèi)存泄露,而程序中真的有這么多內(nèi)存需要被占用(因?yàn)閺拇L問(wèn)隊(duì)列中解析出來(lái)的URL要遠(yuǎn)比它本身要多得多)
? (4)在前幾篇博客中,我就有提到使用Map對(duì)象來(lái)保存URL的訪問(wèn)信息。不過(guò),現(xiàn)在我要否定它。因?yàn)?#xff0c;在長(zhǎng)時(shí)間運(yùn)行之后,Map也是會(huì)占用大量的內(nèi)存。只不過(guò),會(huì)比第3種方式要小一些。下面是使用Map<Integer,?Integer>去重,在長(zhǎng)時(shí)間運(yùn)行中內(nèi)存的使用情況:
?
BloomFilter的使用:
1.一般情況下BloomFilter使用內(nèi)存的情況:
?
2.爬蟲程序中BloomFilter使用內(nèi)存的情況(已運(yùn)行4小時(shí)):
?
3.程序結(jié)構(gòu)圖
?
?
4.BloomFilter的一般使用
? 此處關(guān)于BloomFilter的Java代碼部分,參考于:http://www.cnblogs.com/heaad/archive/2011/01/02/1924195.html
? 如果你看了上面的文章,相信你已經(jīng)了解到布隆過(guò)濾器的空間復(fù)雜度是S(n)=O(n)。關(guān)于這一點(diǎn),相信你已經(jīng)從上面的內(nèi)存使用情況中了解到了這一點(diǎn)。那么以下會(huì)是一些相關(guān)的Java代碼展示。而在查重過(guò)程也很有效率,時(shí)間復(fù)雜度是T(n)=O(1)。
BloomFilter.java
Test.java
public class Test {private final String[] URLS = {"http://www.csdn.net/","http://www.baidu.com/","http://www.google.com.hk","http://www.cnblogs.com/","http://www.zhihu.com/","https://www.shiyanlou.com/","http://www.google.com.hk","https://www.shiyanlou.com/","http://www.csdn.net/"};private void testBloomFilter() {BloomFilter filter = new BloomFilter();for (int i = 0; i < URLS.length; i++) {if (filter.contains(URLS[i])) {System.out.println("contain: " + URLS[i]);continue;}filter.add(URLS[i]);}}public static void main(String[] args) {Test t = new Test();t.testBloomFilter();} }
???????????????????
5.BloomFilter在爬蟲中過(guò)濾重復(fù)的URL
public class ParserRunner implements Runnable {private SpiderSet mResultSet = null;private WebInfoModel mInfoModel = null;private int mIndex;private final boolean DEBUG = false;private SpiderBloomFilter mFlagBloomFilter = null;public ParserRunner(SpiderSet set, WebInfoModel model, int index, SpiderBloomFilter filter) {mResultSet = set;mInfoModel = model;mIndex = index;mFlagBloomFilter = filter;}@Overridepublic void run() {long t = System.currentTimeMillis();SpiderQueue tmpQueue = new SpiderQueue();PythonUtils.fillAddressQueueByPython(tmpQueue, mInfoModel.getAddress(), mInfoModel.getLevel());WebInfoModel model = null;while (!tmpQueue.isQueueEmpty()) {model = tmpQueue.poll();if (model == null || mFlagBloomFilter.contains(model.getAddress())) {continue;}mResultSet.add(model);mFlagBloomFilter.add(model.getAddress());}tmpQueue = null;model = null;System.err.println("Thread-" + mIndex + ", UsedTime-" + (System.currentTimeMillis() - t) + ", SetSize = " + mResultSet.size());t = 0;}@SuppressWarnings("unused")private void sleep(long millis) {try {Thread.sleep(millis);} catch (InterruptedException e) {e.printStackTrace();}} } ? 如果你看過(guò)我之前的博客,那么上面的這一段代碼相信你會(huì)比較熟悉。
? 這段代碼的功能是:生產(chǎn)者。從待訪問(wèn)隊(duì)列中消費(fèi)一個(gè)model,然后調(diào)用Python生產(chǎn)鏈接的列表Queue,并將生成的列表Queue offer到結(jié)果SpiderSet中。
總結(jié)
以上是生活随笔為你收集整理的网络爬虫:URL去重策略之布隆过滤器(BloomFilter)的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 网络爬虫:采用“负载均衡”策略来优化网络
- 下一篇: 网站分类前导:获取网站标题和描述及对相关