利用Http状态码检查网页内容是否更新
在做网页抓取爬虫类的工具时,经常要对页面进行监控和解析,其中监控就是检查页面内容是否发生了更新。判断网页是否发生变化最直接的方法是设置页面的某一处为监控区域,每次都抓取该部分区域的内容,然后与本地保存的或最近一次抓取内容比较,如果有差异就表明网页发生了变化,才可以进行解析。这种方法比较稳妥,几乎可达到万无一失的效果。但是,这种方式在每次扫描时都要下载页面内容,并且要去截取监控区域的内容,最后还要进行字符串比较,整个过程比较耗时。其实在众多网页中,有一部分网站的网页内容是静态页面,如图片,html,js等,这些静态页面往往可能是服务器早已准备好的,用户访问时仅仅是下载而已。那么针对这种静态页面,就可以仅仅通过状态码来判断,内容是否发生了变化。
这个状态码就是304(Not Modified),对于这个代码的解释是"如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码"。很明显通过这个解释,我们就明白了其实现的机理。我们要做的事情,仅仅是在发送请求时将上次访问的时间添加到header中,然后根据服务器返回的状态码进行判断。通常,在网页发生变化时,服务器会返回状态码200,而未发生变化则返回304。
dotnet在网络传输这块已经提供了完善的API,接下来请看具体的实现方式。在这个例子中,通过访问国家统计局的banner页面(http://www.stats.gov.cn/top.html)来演示,分别检查3天前和3个月前该页面是否发生了变化。
{
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内容。经过实际项目的实践,发现大部分的静态页面内容,都能够通过该方法判断。
如有更好的方法,特别是判断动态页面是否更新的方法,欢迎交流。