在Python中维护网页的更新文件缓存?

nea*_*mcb 5 python caching web

我正在编写一个需要持久本地访问通过http获取的大文件的应用程序.我希望保存在本地目录(某种部分镜像)中的文件,以便后续执行应用程序只是注意到URL已经在本地镜像,以便其他程序可以使用它们.

理想情况下,它还可以保留时间戳或etag信息,并能够使用If-Modified-Since或If-None-Match标头快速发出http请求,以检查新版本但避免完全下载,除非文件已更新.但是,由于这些网页很少发生变化,我可能会遇到过时副本中的错误,并且在适当的时候找到其他方法从缓存中删除文件.

环顾四周,我看到urllib.request.urlretrieve可以保存缓存副本,但看起来它无法处理我的If-Modified-Since缓存更新目标.

请求模块似乎是最新的和最好的,但它似乎不适用于这种情况.有一个CacheControl附加模块,它支持我的缓存更新目标,因为它执行完整的HTTP缓存.但似乎它不会以可直接用于其他(非python)程序的方式存储获取的文件,因为FileCache将资源存储为pickle数据.而在python请求的讨论可以直接获取磁盘上的文件句柄,如curl?- Stack Overflow建议可以使用一些额外的代码保存到本地文件,但这似乎与CacheControl模块没有很好的混合.

那么有一个网络抓取库可以满足我的需求吗?这基本上可以维护过去已经获取的文件的镜像(并告诉我文件名是什么),而不必我必须明确地管理所有这些文件?

小智 5

我有相同的要求并找到了requests-cache。添加它非常容易,因为它扩展了requests。您可以将缓存保留在内存中并在脚本结束后消失,也可以使用 sqlite、mongodb 或 redis 使其持久化。这是我写的两行,它的效果如广告所示:

import requests, requests_cache
requests_cache.install_cache('scraper_cache', backend='sqlite', expire_after=3600)
Run Code Online (Sandbox Code Playgroud)


nat*_*ill 2

我认为没有一个库可以做到这一点,但实现起来并不难。以下是我在请求中使用的一些函数,可能会对您有所帮助:

import os
import os.path as op
import requests
import urlparse

CACHE = 'path/to/cache'

def _file_from_url(url):
    return op.basename(urlparse.urlsplit(url).path)


def is_cached(*args, **kwargs):
    url = kwargs.get('url', args[0] if args else None)
    path = op.join(CACHE, _file_from_url(url))

    if not op.isfile(path):
        return False

    res = requests.head(*args, **kwargs)

    if not res.ok:
        return False

    # Check if cache is stale. For me, checking content-length fitted my use case.
    # You can use modification date or etag here:

    if not 'content-length' in res.headers:
        return False

    return op.getsize(path) == int(res.headers['content-length'])


def update_cache(*args, **kwargs):
    url = kwargs.get('url', args[0] if args else None)
    path = op.join(CACHE, _file_from_url(url))

    res = requests.get(*args, **kwargs)

    if res.ok:
        with open(path, 'wb') as handle:
            for block in res.iter_content(1024):
                if block:
                    handle.write(block)
                    handle.flush()
Run Code Online (Sandbox Code Playgroud)

用法:

if not is_cached('http://www.google.com/humans.txt'):
    update_cache('http://www.google.com/humans.txt')

# Do something with cache/humans.txt
Run Code Online (Sandbox Code Playgroud)