在做網頁抓取爬蟲類的工具時,經常要對頁面進行監控和解析,其中監控就是檢查頁面內容是否發生了更新。判斷網頁是否發生變化最直接的方法是設定頁面的某一處為監控地區,每次都抓取該部分地區的內容,然後與本地儲存的或最近一次抓取內容比較,如果有差異就表明網頁發生了變化,才可以進行解析。這種方法比較穩妥,幾乎可達到萬無一失的效果。但是,這種方式在每次掃描時都要下載頁面內容,並且要去截取監控地區的內容,最後還要進行字串比較,整個過程比較耗時。其實在眾多網頁中,有一部分網站的網頁內容是靜態頁面,片,html,js等,這些靜態頁面往往可能是伺服器早已準備好的,使用者訪問時僅僅是下載而已。那麼針對這種靜態頁面,就可以僅僅通過狀態代碼來判斷,內容是否發生了變化。
這個狀態代碼就是304(Not Modified),對於這個代碼的解釋是"如果用戶端發送了一個帶條件的 GET 請求且該請求已被允許,而文檔的內容(自上次訪問以來或者根據請求的條件)並沒有改變,則伺服器應當返回這個狀態代碼"。很明顯通過這個解釋,我們就明白了其實現的機理。我們要做的事情,僅僅是在發送請求時將上次訪問的時間添加到header中,然後根據伺服器返回的狀態代碼進行判斷。通常,在網頁發生變化時,伺服器會返回狀態代碼200,而未發生變化則返回304。
dotnet在網路傳輸這塊已經提供了完善的API,接下來請看具體的實現方式。在這個例子中,通過訪問國家統計局的banner頁面(http://www.stats.gov.cn/top.html)來示範,分別檢查3天前和3個月前該頁面是否發生了變化。
static void Main(string[] args)
{
string url = "http://www.stats.gov.cn/top.html";
// 檢查3天前網頁是否發生了變化
DateTime modifiedSince = DateTime.Now.AddDays(-3);
// 輸出false
Console.WriteLine(PageHasChanged(url, modifiedSince));
// 檢查3個月前是否發生了變化
modifiedSince = DateTime.Now.AddMonths(-3);
// 輸出true
Console.WriteLine(PageHasChanged(url, modifiedSince));
}
private static bool PageHasChanged(string url, DateTime modifiedSince)
{
bool changed = false;
// 佈建要求資訊
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
// 最關鍵的一項設定 將請求的IfModifiedSince設定為指定的時間
request.IfModifiedSince = modifiedSince;
HttpWebResponse response = null;
try
{
response = request.GetResponse() as HttpWebResponse;
// 根據返回的狀態代碼判斷內容是否發生了變化
// 200表示頁面發生了變化
if (response.StatusCode == HttpStatusCode.OK)
{
changed = true;
}
else if (response.StatusCode == HttpStatusCode.NotModified)
{
changed = false;
}
response.Close();
}
catch (WebException ex)
{
// 對於沒有發生變化的頁面 通常會通過拋出異常的方式提示頁面未發生變化
if ((ex.Response as HttpWebResponse).StatusCode == HttpStatusCode.NotModified)
{
changed = false;
}
else
{
throw ex;
}
}
return changed;
}
需要注意的是,在網頁內容沒有發生變化時,會拋出異常,狀態代碼只能在異常資訊中擷取。另外在返回200狀態代碼時,有時候並不表示頁面發生了變化,因為有些伺服器不識別請求中的lastModifiedSince內容。經過實際項目的實踐,發現大部分的靜態頁面內容,都能夠通過該方法判斷。
如有更好的方法,特別是判斷動態網頁面是否更新的方法,歡迎交流。