Python: оптимальный алгоритм, позволяющий избежать загрузки неизмененных страниц при сканировании

Я пишу сканеру, который регулярно проверяет список новостных сайтов на наличие новых статей. Я читал о различных подходах для избежания ненужных загрузок страниц, в основном идентифицировал 5 элементов заголовка, которые могут быть полезны, чтобы определить, изменилась страница или нет:

  1. Статус HTTP
  2. ETAG
  3. Last_modified (объединить с запросом If-Modified-Since)
  4. Истекает
  5. Content-Length.

Отличный http://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

2 ответа

Решение

Единственное, что вам нужно проверить, прежде чем сделать запрос Expires, If-Modified-Since это не то, что сервер отправляет вам, а то, что вы отправляете на сервер.

Что вы хотите сделать, это HTTP GET с If-Modified-Since заголовок, указывающий, когда вы в последний раз получили ресурс. Если вы вернетесь код состояния 304 а не обычный 200с тех пор ресурс не был изменен, и вам следует использовать сохраненную копию (новая копия не будет отправлена).

Кроме того, вы должны сохранить Expires заголовок с последнего раза, когда вы получили документ, а не выдавать GET вообще, если ваша сохраненная копия документа не истек.

Перевод этого в Python оставлен в качестве упражнения, но добавить добавление If-Modified-Since заголовок к запросу, чтобы сохранить Expires заголовок из ответа, и проверить код состояния из ответа.

Вы должны были бы передать в заголовке 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()
Другие вопросы по тегам