0%

如何判断一个页面是否更新?

对于需要长期运行的爬虫程序,在每次爬取前检查目标页面是否更新是十分重要的。如果不进行判断,就会对同一个页面重复爬取多次,浪费时间和性能。

定时爬取

这种方式即为最传统的方式,无论页面是否更新,都会每隔一段时间对页面爬取一次。
同时,根据定时方式,又分为在程序内手动轮询计时和使用第三方定时程序两种。

不推荐使用这种方法,效率低下。一定要用的话也建议使用 crontab 等其他程序定时调用,尽可能的在等待时间内不占用系统资源。


304 HTTP 状态码

HTTP 头字段中定义了一个 304 状态码 ( 304 Not Modified ),这个请求头标签原本是供浏览器缓存用的,现代浏览器通常会缓存网页文件在自己的数据目录中,以便下次能更快的加载。

在浏览器请求页面时,会把本地缓存的网页文件的最后修改时间放在请求头一起发送到服务器。服务器根据这个时间判断,如果页面有更新,会返回 HTTP 状态码 200,正常响应。如果页面没有更新,会返回 HTTP 状态码 304(不返回网页内容,只返回响应头)。

那么作为爬虫,就可以模拟浏览器的这个行为,在本次爬取后记录爬取的时间,下一次爬取前在请求头中放入 If-Modified-Since,把上次爬取的时间一起发送给服务器,然后根据响应的 HTTP 状态码进行下一步操作。

这种方法看似完美,但是很可惜,因为并不是所有服务器都会遵守规范返回 304 状态码。对于后端渲染的动态页面,或是前端渲染的单页面应用,这种方法也不合适。


Last-Modified 响应字段

根据 HTTP 规范,响应头中还有一个 Last-Modified 字段来返回页面最后一次的修改时间。
将这个时间与上一次爬取的时间做对比即可判断出页面是否更新。

很可惜,这种方法基本只适用于静态页面。对于动态页面来说,返回的最后修改时间就是发回 Response 的时间,因为动态页面本来就是即时渲染的。


Etag 响应字段

Etag 主要为了解决前两个无法解决的一些问题而被创造出来的,例如:

  • 一些文件也许仅仅改变了修改时间,内容并没有改变
  • If-Modified-Since 的检查粒度是秒级的,无法做到更精确的检查
  • 服务器无法控制

支持 Etag 的服务器会像返回 Last-Modified 那样返回一个Etag 字符串,客户端请求时根据需要在请求头的 If-MatchIf-None-Match(更常用)字段中放入上次请求得来的 Etag 字符串,然后服务器会根据 Etag 作出与 If-Modified-Since 同样的响应。

这种方法的最大问题就是服务器不一定支持。Etag 本身也是服务器根据一定规则计算出来的,有计算就有性能消耗,就看服务器愿不愿意支持了。


基于页面地址的比较

在本次爬取后,保存爬取的 URL,一段时间内爬过的就不用再爬了。如果页面 URL 中有诸如 ID 之类的参数的话,可以使用 ID,因为 ID 在后端中通常是唯一的。经常与 定时爬取 配合使用。


基于页面内容的比较

在本次爬取后,抽取页面关键元素的内容计算 MD5 并保存,下次爬取时比较 MD5 判断是否更新。如果关键内容不多的话,也可以不做 MD5 直接保存。

不建议直接保存整个页面全部内容或其 MD5,因为如果页面中有 本站已运行 xx 时间、访客:xx 之类不断更新的内容,就会造成误判。