【问题标题】:Python: Optimal algorithm to avoid downloading unchanged pages while crawlingPython:避免在抓取时下载未更改页面的最佳算法
【发布时间】:2011-11-28 04:12:09
【问题描述】:

我正在编写一个爬虫,它会定期检查新闻网站列表中的新文章。 我已经阅读了有关避免不必要页面下载的不同方法,基本上确定了 5 个标题元素,这些元素可能有助于确定页面是否已更改:

  1. HTTP 状态
  2. ETAG
  3. Last_modified(与 If-Modified-Since 请求结合使用)
  4. 过期
  5. 内容长度。

优秀的FeedParser.org 似乎实现了其中一些方法。

我正在寻找做出这种决定的 Python(或任何类似语言)的最佳代码。 请记住,标头信息始终由服务器提供。

可能是这样的:

def shouldDonwload(url,prev_etag,prev_lastmod,prev_expires, prev_content_length):
    #retrieve the headers, do the magic here and return the decision
    return decision

【问题讨论】:

    标签: python http-headers etag web-crawler if-modified-since


    【解决方案1】:

    在发出请求之前,您唯一需要检查的是ExpiresIf-Modified-Since 不是服务器发给你的,而是你发给服务器的。

    您想要做的是一个带有If-Modified-Since 标头的HTTP GET,指示您上次检索资源的时间。如果您返回状态码304 而不是通常的200,则表示该资源从那时起没有被修改过,您应该使用您存储的副本(不会发送新副本)。

    此外,您应该保留上次检索文档时的 Expires 标头,如果您存储的文档副本尚未过期,则根本不要发出 GET

    将其翻译成 Python 留作练习,但在请求中添加 If-Modified-Since 标头、存储响应中的 Expires 标头以及检查响应中的状态代码应该很简单。

    【讨论】:

    • 您的回答暗示不需要关心 ETAG 吗?那么内容长度呢?无论其他参数如何,都不是决定再次下载的最终方式吗?
    • 如果 Content-LengthETAG 更改,则文档已修改,If-Modified-Since GET 请求将返回文档。你也可以在If-None-Match 标头中使用ETAG,但对我来说两者都使用似乎是多余的,而且ETAG 是可选的,所以我会选择IMS
    【解决方案2】:

    您需要将标题字典传递给shouldDownload(或urlopen 的结果):

    def shouldDownload(url, headers, prev_etag, prev_lastmod, prev_expires,  prev_content_length):
        return (prev_content_length != headers.get("content-length") || prev_lastmod != headers.get("If-Modified-Since") || prev_expires != headers.get("Expires") || prev_etag != headers.get("ETAG"))
        # or the optimistic way:
        # return prev_content_length == headers.get("content-length") and prev_lastmod == headers.get("If-Modified-Since") and prev_expires = headers.get("Expires") and prev_etag = headers.get("ETAG")
    

    在打开 URL 时执行此操作:

    # my urllib2 is a little fuzzy but I believe `urlopen()` doesn't 
    #  read the whole file until `.read()` is called, and you can still 
    #  get the headers with `.headers`.  Worst case is you may have to 
    #  `read(50)` or so to get them.
    s = urllib2.urlopen(MYURL)
    try:
        if shouldDownload(s.headers):
            source = s.read()
            # do stuff with source
       else:
            continue
    # except HTTPError, etc if you need to  
    finally:
        s.close()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      • 1970-01-01
      • 2018-02-25
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多