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

Я пишу сканеру, который регулярно проверяет список новостных сайтов на наличие новых статей.Я читал о различных подходах, позволяющих избежать ненужных загрузок страниц, в основном выделил 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

Ответы [ 2 ]

2 голосов
/ 30 сентября 2011

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

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

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

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

1 голос
/ 30 сентября 2011

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