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

2024-06-16 12:30:58 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

环顾四周,我看到了urllib.request.urlretrieve可以保存缓存的副本,但似乎无法处理我的If-Modified-Since-cache更新目标。在

请求模块似乎是最新和最棒的,但它似乎不适用于本例。有一个CacheControl附加模块,它支持我的缓存更新目标,因为它执行完整的HTTP缓存。但它似乎没有以可直接用于其他(非python)程序的方式存储获取的文件,因为FileCache将资源存储为pickled数据。在can python-requests fetch url directly to file handle on disk like curl? - Stack Overflow上的讨论表明,保存到本地文件可以使用一些额外的代码来完成,但这似乎并不能很好地与CacheControl模块结合起来。在

那么有没有一个网上取书的图书馆可以满足我的需要?它基本上可以维护过去获取的文件的镜像(并告诉我文件名是什么),而不必显式地管理所有这些文件?在


Tags: 模块文件程序目录应用程序httpurl目标
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()

用法:

^{pr2}$

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

import requests, requests_cache
requests_cache.install_cache('scraper_cache', backend='sqlite', expire_after=3600)

相关问题 更多 >