Nutch 1.3 學習筆記 8 LinkDb
----------------------------
這裡主要是分析一下org.apache.nutch.crawl.LinkDb,它主要是用計算反向連結。
1. 運行命令 bin/nutch invertlinks
協助參數說明:
Usage: LinkDb <linkdb> (-dir <segmentsDir> | <seg1> <seg2> ...) [-force] [-noNormalize] [-noFilter]linkdboutput LinkDb to create or update-dir segmentsDirparent directory of several segments, ORseg1 seg2 ... list of segment directories-forceforce update even if LinkDb appears to be locked (CAUTION advised)-noNormalizedon't normalize link URLs-noFilterdon't apply URLFilters to link URLs
本地的運行結果為:
lemo@debian:~/Workspace/java/Apache/Nutch/nutch-1.3$ bin/nutch invertlinks db/linkdb/ db/segments/20110822105243/ LinkDb: starting at 2011-08-29 09:21:36LinkDb: linkdb: db/linkdbLinkDb: URL normalize: trueLinkDb: URL filter: trueLinkDb: adding segment: db/segments/20110822105243 // 加入新的segment庫LinkDb: merging with existing linkdb: db/linkdb // 與原因的庫進行合并LinkDb: finished at 2011-08-29 09:21:40, elapsed: 00:00:03
2. LinkDb主要原始碼分析
在LinkDb主要是調用一個invert方法,這個方法做了兩件事,
+ 分析新輸入的segment目錄,產生新的反向連結庫
+ 把新產生的反向連結庫與原來的庫進行合并
2.1 分析新輸入的segment目錄,主要代碼如下:
// 建立立一個MP任務JobConf job = LinkDb.createJob(getConf(), linkDb, normalize, filter);// 添加目錄到輸入路徑,這裡可能有多個輸入路徑, parse_data for (int i = 0; i < segments.length; i++) { if (LOG.isInfoEnabled()) { LOG.info("LinkDb: adding segment: " + segments[i]); } FileInputFormat.addInputPath(job, new Path(segments[i], ParseData.DIR_NAME)); }// 提交MP任務 try { JobClient.runJob(job); } catch (IOException e) { LockUtil.removeLockFile(fs, lock); throw e; }
下面來看一下createJob都做了些什麼:
private static JobConf createJob(Configuration config, Path linkDb, boolean normalize, boolean filter) {// 新成一個臨時的目錄 Path newLinkDb = new Path("linkdb-" + Integer.toString(new Random().nextInt(Integer.MAX_VALUE))); JobConf job = new NutchJob(config); job.setJobName("linkdb " + linkDb);// 設定輸出格式 job.setInputFormat(SequenceFileInputFormat.class);// 配置Map,Combiner,Reducer方法 job.setMapperClass(LinkDb.class); job.setCombinerClass(LinkDbMerger.class);// 如果配置了過濾或者規格化,並且沒有找到老的linkdb目錄,好就以filter和normalize進行配置 // if we don't run the mergeJob, perform normalization/filtering now if (normalize || filter) { try { FileSystem fs = FileSystem.get(config); if (!fs.exists(linkDb)) { job.setBoolean(LinkDbFilter.URL_FILTERING, filter); job.setBoolean(LinkDbFilter.URL_NORMALIZING, normalize); } } catch (Exception e) { LOG.warn("LinkDb createJob: " + e); } } job.setReducerClass(LinkDbMerger.class);// 配置MP輸出路徑 FileOutputFormat.setOutputPath(job, newLinkDb);// 配置輸出格式 job.setOutputFormat(MapFileOutputFormat.class);// 對map輸出使用壓縮,以減少Reducer的輸入壓力 job.setBoolean("mapred.output.compress", true);// 配置<key,value>的輸出類型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(Inlinks.class); return job; }
下面來看一下LinkDb中的map做了些什麼,這個方法主要是從toUrl=>fromUrl建立一個對應關係,這個有點像倒排索引中的TermId=>DocId
而LinkDbMerger這個類實現了reducer介面,主要是收集指定個數的同一個toUrl的fromUrl,這個指定的個數可能通過設定db.max.inlinks來設定。
2.2 把新產生的反向連結庫與老的庫進行合并,主要代碼如下:
if (fs.exists(currentLinkDb)) { // 如果存在老的反向連結庫,就進行合并 if (LOG.isInfoEnabled()) { LOG.info("LinkDb: merging with existing linkdb: " + linkDb); } // try to merge // Path newLinkDb = FileOutputFormat.getOutputPath(job); job = LinkDbMerger.createMergeJob(getConf(), linkDb, normalize, filter); // 加入輸入路徑 FileInputFormat.addInputPath(job, currentLinkDb); FileInputFormat.addInputPath(job, newLinkDb); try { JobClient.runJob(job); } catch (IOException e) { LockUtil.removeLockFile(fs, lock); fs.delete(newLinkDb, true); throw e; } fs.delete(newLinkDb, true); } LinkDb.install(job, linkDb); // 安裝新產生的反向連結庫
我們再看一下createMergeJob做了些什麼:
public static JobConf createMergeJob(Configuration config, Path linkDb, boolean normalize, boolean filter) { // 產生一個臨時目錄 Path newLinkDb = new Path("linkdb-merge-" + Integer.toString(new Random().nextInt(Integer.MAX_VALUE))); JobConf job = new NutchJob(config); job.setJobName("linkdb merge " + linkDb);// 配置個輸出格式 job.setInputFormat(SequenceFileInputFormat.class);// 配置Map與Reducer方法,這裡的Reducer方法與上面的一樣,用於對相同key(toUrl)的values進行彙總// 然後輸出指定個數的value,而這裡的LinkDbFilter應該是對key與value所對應的url進行過濾與正規化 job.setMapperClass(LinkDbFilter.class); job.setBoolean(LinkDbFilter.URL_NORMALIZING, normalize); job.setBoolean(LinkDbFilter.URL_FILTERING, filter); job.setReducerClass(LinkDbMerger.class);// 配置輸出路徑 FileOutputFormat.setOutputPath(job, newLinkDb); job.setOutputFormat(MapFileOutputFormat.class); job.setBoolean("mapred.output.compress", true); job.setOutputKeyClass(Text.class); job.setOutputValueClass(Inlinks.class); return job; }
3. bin/nutch readlinkdb 分析
主要是用於下載linkdb的內容到指定的目錄,協助如下:
Usage: LinkDbReader <linkdb> {-dump <out_dir> | -url <url>)-dump <out_dir>dump whole link db to a text file in <out_dir>-url <url>print information about <url> to System.out
下面是本機啟動並執行結果:
lemo@debian:~/Workspace/java/Apache/Nutch/nutch-1.3$ bin/nutch readlinkdb db/linkdb/ -dump output2LinkDb dump: starting at 2011-08-29 09:54:08LinkDb dump: db: db/linkdb/LinkDb dump: finished at 2011-08-29 09:54:09, elapsed: 00:00:01
下面是輸出output2目錄中檔案的部分內容,可以看到,這裡一個<key,value>對,key是toUrl,value是fromUrl
lemo@debian:~/Workspace/java/Apache/Nutch/nutch-1.3$ head output2/part-00000 http://baike.baidu.com/Inlinks: fromUrl: http://www.baidu.com/ anchor: 百科http://hi.baidu.com/Inlinks: fromUrl: http://www.baidu.com/ anchor: 空間http://hi.baidu.com/baidu/Inlinks: fromUrl: http://www.baidu.com/ anchor: http://home.baidu.com/Inlinks:
這個readlinkdb也是用了一個MP任務,輸入格式為SequenceFileInputFormat,輸出格式為TextOutput,Map-Reducer都是用的預設的。
4. bin/nutch mergelinkdb 分析
主要是用來合并個不同的linkdb資料庫的
Usage: LinkDbMerger <output_linkdb> <linkdb1> [<linkdb2> <linkdb3> ...] [-normalize] [-filter]output_linkdboutput LinkDblinkdb1 ...input LinkDb-s (single input LinkDb is ok)-normalizeuse URLNormalizer on both fromUrls and toUrls in linkdb(s) (usually not needed)-filteruse URLFilters on both fromUrls and toUrls in linkdb(s)
其實這裡的合并就是調用上面分析的LinkDbMerger中的createMergeJob方法
5. 總結
這裡主要是對於parse_data目錄中分析出來的外連結進行反向連結的計算,在下面計算連結分數的時候會用到這些反向連結。