网络爬虫:基于对象持久化实现爬虫现场快速还原
前言:
? 因?yàn)橹虚g有一些其他的任務(wù)工作,所以有一些時(shí)日沒有再關(guān)心爬蟲的程序了。今天想到了另一個(gè)優(yōu)化爬蟲的思路。
? 在上篇中,我們說(shuō)到可以使用布隆過(guò)濾器可以很好地實(shí)現(xiàn)URL的去重操作。可是,如果在某一個(gè)時(shí)刻我們不小心中止了爬蟲的繼續(xù)運(yùn)行。這個(gè)時(shí)候要怎么辦呢?
? 本篇博客的重點(diǎn)正是解決這個(gè)問(wèn)題。
本文鏈接:http://blog.csdn.net/lemon_tree12138/article/details/50069047 -- Coding-Naga
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?--轉(zhuǎn)載請(qǐng)注明出處
問(wèn)題描述:
? 上面也有提到,現(xiàn)在假設(shè)我們的程序需要在中途暫停一下。這樣,會(huì)直接導(dǎo)致一個(gè)問(wèn)題,我們的程序無(wú)法保留之前使用BloomFilter保存的URL信息。如果你對(duì)BloomFilter還不了解,歡迎移步到我的上一篇博客《網(wǎng)絡(luò)爬蟲:URL去重策略之布隆過(guò)濾器(BloomFilter)的使用》了解一下。
? 對(duì)于爬蟲程序中使用兩個(gè)隊(duì)列“對(duì)象”是好處理的,因?yàn)檫@部分?jǐn)?shù)據(jù)是直接存放在數(shù)據(jù)庫(kù)中(磁盤里)的。這個(gè)不用擔(dān)心。可是如果這個(gè)BloomFilter如果沒有得到一個(gè)很好的處理就是一個(gè)比較麻煩的事情了,這使得我們?cè)诤笃诔绦驁?zhí)行的過(guò)程中無(wú)法很準(zhǔn)確地判斷一個(gè)URL是否有訪問(wèn)過(guò),這樣程序的效率勢(shì)必會(huì)受到不了的影響。這里我提供了兩種解決方案。當(dāng)然一種是好的解決方法,一種是不那么好的解決方法。
前一種處理方案:
? 這里我想到的是如何通過(guò)現(xiàn)在有的數(shù)據(jù)(數(shù)據(jù)庫(kù)中的數(shù)據(jù))信息,盡可能完整地構(gòu)建原來(lái)的BloomFilter。我的做法是在程序重新啟動(dòng)的時(shí)候去讀數(shù)據(jù)庫(kù),把數(shù)據(jù)庫(kù)中的信息一個(gè)一個(gè)地往過(guò)濾器中填。試想一下,如果這個(gè)時(shí)候數(shù)據(jù)庫(kù)中有千萬(wàn)級(jí)的數(shù)據(jù),我們也要一個(gè)一個(gè)地往里填,這樣勢(shì)必有點(diǎn)太耗時(shí)了。
? 因?yàn)檫@里我們是需要先從數(shù)據(jù)庫(kù)中去獲得數(shù)據(jù),再將數(shù)據(jù)添加到過(guò)濾器中。這兩步都是耗時(shí)的操作,所以,如果能不用這種方法就不用這種方法,這是下下策。
對(duì)象持久化方案:
1.格式化數(shù)據(jù)保存到文件
? 從之前的博客中,我們可以知道BloomFilter的核心是一個(gè)很長(zhǎng)的數(shù)組,這個(gè)數(shù)組是保存在BitSet中。那么這里我們就可以把這么多位的每一位保存到文件或是數(shù)據(jù)庫(kù)中。這樣在程序啟動(dòng)的時(shí)候就可以直接讀入了。關(guān)于這個(gè)想法,我猜是可行的。之所以說(shuō)是“猜”,因?yàn)槲乙矝]有使用過(guò)這樣方法。感覺是Ok的,不過(guò)沒實(shí)踐過(guò),如果讀者感興趣可以試試看。這里就不多說(shuō)了,說(shuō)這個(gè)思路的目的,主要還是為了引出下面的這種方法。
2.基于Serializable的實(shí)現(xiàn)
思路分析:
? 說(shuō)過(guò)了下下策和保存到文件這兩種,是不是這里可以說(shuō)一下上上策了?因?yàn)檫€不知道有沒有更好的方法,所以上上策還不敢斷言,不過(guò)這里要說(shuō)的可以說(shuō)是上策。我們?cè)趯W(xué)習(xí)可序列化類Serializable的時(shí)候,應(yīng)該就已經(jīng)知道了這個(gè)類可以讓一個(gè)對(duì)象固化到磁盤,也就是說(shuō)這個(gè)對(duì)象我們可以把它保存到磁盤上。下次在需要用的時(shí)候再去讀一下就OK了。所以,這里我們就可以這樣來(lái)做。
? 首先,我們需要讓BloomFilter及其相關(guān)類實(shí)現(xiàn)Serializable接口,因?yàn)檫@些對(duì)象需要被持久化。并且添加上serialVersionUID成員常量。
保存到磁盤:
/*** 將一個(gè)對(duì)象寫入到磁盤* * @param s* 待寫入的對(duì)象* @param path* 寫入的路徑*/public static void writeObject(Serializable s, String path) {try {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));objectOutputStream.writeObject(s);objectOutputStream.close();} catch (IOException e) {e.printStackTrace();}}
從磁盤中讀取對(duì)象:
public static Object readObject(String path) {Object object = null;try {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));object = objectInputStream.readObject();objectInputStream.close();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return object;}測(cè)試過(guò)程:
? 測(cè)試的方法很簡(jiǎn)單,我們先在過(guò)濾中添加一些數(shù)據(jù)。并將持有數(shù)據(jù)的過(guò)濾器對(duì)象寫到磁盤中。在我們需要的時(shí)候去讀取磁盤上保存的對(duì)應(yīng)文件即可。代碼邏輯如下:
public class BloomFilterTest {public static void main(String[] args) {String path = "F:/Temp/bloom.obj";BloomFilterTest test = new BloomFilterTest();test.testWriteBloomFilter(path);BloomFilter readFilter = test.testReadBloomFilter(path);boolean b1 = readFilter.contains("baidu");boolean b2 = readFilter.contains("google");boolean b3 = readFilter.contains("naga");boolean b4 = readFilter.contains("hello");boolean b5 = readFilter.contains("world");boolean b6 = readFilter.contains("java");System.out.println(b1);System.out.println(b2);System.out.println(b3);System.out.println(b4);System.out.println(b5);System.out.println(b6);}private void testWriteBloomFilter(String path) {BloomFilter filter = new BloomFilter();filter.add("baidu");filter.add("google");filter.add("naga");filter.add("hello");filter.add("world");SerializationUtils.writeObject(filter, path);}private BloomFilter testReadBloomFilter(String path) {Object object = SerializationUtils.readObject(path);return (BloomFilter)object;} }
測(cè)試結(jié)果:
? 我們?cè)谶^(guò)濾器中添加了"baidu",?"google",?"naga",?"hello",?"world"這些字符串值。在驗(yàn)證的時(shí)候,我們多驗(yàn)證了一個(gè)"java"字符串。如果方案可行,我們將獲得5個(gè)true和1個(gè)false的結(jié)果。以下是測(cè)試結(jié)果:true true true true true false 由此驗(yàn)證此方法可行。
注意事項(xiàng):
? 1.過(guò)濾器內(nèi)部的SimpleHash內(nèi)部類也需要實(shí)現(xiàn)Serializable接口。因?yàn)檫@個(gè)SimpleHash也有對(duì)象在過(guò)濾器中,在持久化的時(shí)候,SimpleHash對(duì)象也會(huì)被持久化到磁盤;
? 2.本文的測(cè)試實(shí)例,可以在下面GitHub工程的org.naga.demo.bloom包下獲得;
? 3.本方案的作用點(diǎn)是在于停止程序的后勤工作。所以,必須保證程序能夠完成這些后勤工作。也就是說(shuō),我們不能突然去停止程序的運(yùn)行,這樣程序因?yàn)閬?lái)不及保存數(shù)據(jù)而讓BloomFilter對(duì)象持久化失敗。如果想要規(guī)避這個(gè)問(wèn)題,就必須要作出一些其他的犧牲——性能下降。我們可以通過(guò)定時(shí)給BloomFilter進(jìn)行持久化,這樣如果程序被突然中止,也只是會(huì)損失一部分?jǐn)?shù)據(jù)的記錄,不會(huì)造成很大的影響。因?yàn)?#xff0c;這樣會(huì)是程序的性能有所下降,所以如何取舍還是要看需求了。
測(cè)試源碼工程GitHub鏈接:
https://github.com/William-Hai/SimpleDemo
總結(jié)
以上是生活随笔為你收集整理的网络爬虫:基于对象持久化实现爬虫现场快速还原的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数据结构:关于重建二叉树的三种思路
- 下一篇: Java设计模式——工厂模式