今天我們看看Nutch網頁抓取,所用的幾種資料結構:
主要涉及到了這幾個類:FetchListEntry,Page,
首先我們看看FetchListEntry類:
public final class FetchListEntry implements Writable, Cloneable
實作了Writable, Cloneable接口,Nutch許多類實作了Writable, Cloneable。
自己負責自己的讀寫操作其實是個很合理的設計方法,分離出來反倒有很瑣碎
的感覺。
看看裡面的成員變量:
[code]
public static final String DIR_NAME = "fetchlist";//要寫入磁盤的目錄
private final static byte CUR_VERSION = 2;//目前的版本号
private boolean fetch;//是否抓取以便以後更新
private Page page;//目前抓取的頁面
private String[] anchors;//抓取到的該頁面包含的連結
[/code]
我們看看如何讀取各個字段的,也就是函數
public final void readFields(DataInput in) throws IOException
讀取version 字段,并判斷如果版本号是否大約目前的版本号,則抛出版本不比對的異常,
然後讀取fetch 和page 字段。
判斷如果版本号大于1,說明anchors已經儲存過了,讀取anchors,否則直接指派一個空的字元串
代碼如下:
[code]
byte version = in.readByte(); // read version
if (version > CUR_VERSION) // check version
throw new VersionMismatchException(CUR_VERSION, version);
fetch = in.readByte() != 0; // read fetch flag
page = Page.read(in); // read page
if (version > 1) { // anchors added in version 2
anchors = new String[in.readInt()]; // read anchors
for (int i = 0; i < anchors.length; i++) {
anchors[i] = UTF8.readString(in);
}
} else {
anchors = new String[0];
}
[/code]
同時還提供了一個靜态的讀取各個字段的函數,并建構出FetchListEntry對象傳回:
[code]
public static FetchListEntry read(DataInput in) throws IOException {
FetchListEntry result = new FetchListEntry();
result.readFields(in);
return result;
}
[/code]
寫得代碼則比較易看,分别寫每個字段:
[code]
public final void write(DataOutput out) throws IOException {
out.writeByte(CUR_VERSION); // store current version
out.writeByte((byte)(fetch ? 1 : 0)); // write fetch flag
page.write(out); // write page
out.writeInt(anchors.length); // write anchors
for (int i = 0; i < anchors.length; i++) {
UTF8.writeString(out, anchors[i]);
}
}
[/code]
其他的clone和equals函數實作的也非常易懂。
下面我們看看Page類的代碼:
public class Page implements WritableComparable, Cloneable
和FetchListEntry一樣同樣實作了Writable, Cloneable接口,我們看看Nutch的注釋,我們就非常容易知道各個字段的意義了:
[code]
[/code]
各個字段:
[code]
private final static byte CUR_VERSION = 4;
private static final byte DEFAULT_INTERVAL =
(byte)NutchConf.get().getInt("db.default.fetch.interval", 30);
private UTF8 url;
private MD5Hash md5;
private long nextFetch = System.currentTimeMillis();
private byte retries;
private byte fetchInterval = DEFAULT_INTERVAL;
private int numOutlinks;
private float score = 1.0f;
private float nextScore = 1.0f;
[/code]
同樣看看他是如何讀取自己的各個字段的,其實代碼加上本來提供的注釋,使很容易看懂的,不再詳述:
[code]
ublic void readFields(DataInput in) throws IOException {
byte version = in.readByte(); // read version
if (version > CUR_VERSION) // check version
throw new VersionMismatchException(CUR_VERSION, version);
url.readFields(in);
md5.readFields(in);
nextFetch = in.readLong();
retries = in.readByte();
fetchInterval = in.readByte();
numOutlinks = (version > 2) ? in.readInt() : 0; // added in Version 3
score = (version>1) ? in.readFloat() : 1.0f; // score added in version 2
nextScore = (version>3) ? in.readFloat() : 1.0f; // 2nd score added in V4
}
[/code]
寫各個字段也很直接:
[code]
public void write(DataOutput out) throws IOException {
out.writeByte(CUR_VERSION); // store current version
url.write(out);
md5.write(out);
out.writeLong(nextFetch);
out.write(retries);
out.write(fetchInterval);
out.writeInt(numOutlinks);
out.writeFloat(score);
out.writeFloat(nextScore);
}
[/code]
我們順便看看提供友善讀寫Fetch到的内容的類FetcherOutput:這個類通過委托前面介紹的兩個類的讀寫,提供了Fetche到的
各種粒度結構的讀寫功能,代碼都比較直接,不再詳述。
下次我們看看parse-html插件,看看Nutch是如何提取html頁面的。