Frage

Gibt es einen einfachen Weg, um Dinge cachen bei der Verwendung von urllib2, dass ich über aussehende, oder muss ich meine eigene Rolle?

War es hilfreich?

Lösung

Sie könnten eine Dekorateur Funktion wie:

class cache(object):
    def __init__(self, fun):
        self.fun = fun
        self.cache = {}

    def __call__(self, *args, **kwargs):
        key  = str(args) + str(kwargs)
        try:
            return self.cache[key]
        except KeyError:
            self.cache[key] = rval = self.fun(*args, **kwargs)
            return rval
        except TypeError: # incase key isn't a valid key - don't cache
            return self.fun(*args, **kwargs)

und eine Funktion entlang der Linien definieren:

@cache
def get_url_src(url):
    return urllib.urlopen(url).read()

Dies wird vorausgesetzt, Sie nicht die Aufmerksamkeit auf HTTP-Cache-Kontrollen zahlen, wollen aber nur die Seite für die Dauer der Anwendung zwischenzuspeichern

Andere Tipps

Wenn Sie nichts dagegen nicht auf einem etwas niedrigeren Niveau arbeiten, httplib2 ( https://github.com/ httplib2 / httplib2 ) ist eine ausgezeichnete HTTP-Bibliothek, die Caching-Funktionalität enthält.

Dieses Activestate Python Rezept könnte hilfreich sein: http://code.activestate.com/recipes/491261/

Ich habe immer zwischen der Verwendung von httplib2 gerissen worden, die den Umgang mit HTTP-Caching und Authentifizierung eine solide Arbeit leistet, und urllib2, die in der stdlib ist, hat eine erweiterbare Schnittstelle und unterstützen HTTP-Proxy-Server.

Die Activestate Rezept Cachen Unterstützung urllib2 hinzufügen startet, aber nur in einem sehr primitiven Mode. Es schlägt fehl, für Erweiterbarkeit in Speichermechanismen zu ermöglichen, Hartcodierung der Datei-System-backed Speicher. Es ist auch nicht HTTP-Cache-Header ehren.

In einem Versuch zusammen zu bringen die besten Eigenschaften von httplib2 Caching und urllib2 Erweiterbarkeit, habe ich das Activestate Rezept angepasst meisten die gleichen Caching-Funktionalität zu implementieren, wie in httplib2 gefunden wird. Das Modul ist in jaraco.net als jaraco.net .http.caching . Der Link verweist auf das Modul, wie es zum Zeitpunkt des Schreibens dieses Artikels besteht. Während das Modul zur Zeit ein Teil des größeren jaraco.net Paket ist, hat es keine intra-Paketabhängigkeiten, so fühlen sich um das Modul zu ziehen frei heraus und verwenden Sie es in Ihren eigenen Projekten.

Alternativ, wenn Sie Python 2.6 oder höher haben, können Sie easy_install jaraco.net>=1.3 und dann die CachingHandler mit so etwas wie der Code in caching.quick_test() nutzen.

"""Quick test/example of CacheHandler"""
import logging
import urllib2
from httplib2 import FileCache
from jaraco.net.http.caching import CacheHandler

logging.basicConfig(level=logging.DEBUG)
store = FileCache(".cache")
opener = urllib2.build_opener(CacheHandler(store))
urllib2.install_opener(opener)
response = opener.open("http://www.google.com/")
print response.headers
print "Response:", response.read()[:100], '...\n'

response.reload(store)
print response.headers
print "After reload:", response.read()[:100], '...\n'

Beachten Sie, dass jaraco.util.http.caching nicht eine Spezifikation für den Sicherungsspeicher für den Cache liefert, sondern folgt der Schnittstelle von httplib2 verwendet. Aus diesem Grunde kann die httplib2.FileCache direkt mit urllib2 und dem CacheHandler verwendet werden. Auch andere Träger Caches für httplib2 entworfen sollte von dem CacheHandler verwendbar sein.

Ich war auf der Suche nach etwas ähnlichem, und kam über „Rezept 491261: Caching und Drosselung für urllib2“ die geschrieben danivo. Das Problem ist, I wirklich Wenigsten den Caching-Code (eine Menge Doppelarbeit, viele von Dateipfaden manuell verbinden anstatt mit os.path.join verwendet staticmethods, nicht sehr PEP8'sih, und andere Dinge, die ich versuche zu vermeiden)

Der Code ist ein bisschen schöner (meiner Meinung nach sowieso) und ist funktionell sehr ähnlich, mit einigen Zusätzen - vor allem des „Recache“ Verfahren (zB Verwendung sein kann, hier scheint oder im if __name__ == "__main__": Abschnitt am Ende des Codes).

Die neueste Version kann unter http://github.com/ finden dbr / tvdb_api / Blob / Master / cache.py , und ich werde es hier für die Nachwelt einfügen (mit meinem anwendungsspezifischen Header entfernt):

#!/usr/bin/env python
"""
urllib2 caching handler
Modified from http://code.activestate.com/recipes/491261/ by dbr
"""

import os
import time
import httplib
import urllib2
import StringIO
from hashlib import md5

def calculate_cache_path(cache_location, url):
    """Checks if [cache_location]/[hash_of_url].headers and .body exist
    """
    thumb = md5(url).hexdigest()
    header = os.path.join(cache_location, thumb + ".headers")
    body = os.path.join(cache_location, thumb + ".body")
    return header, body

def check_cache_time(path, max_age):
    """Checks if a file has been created/modified in the [last max_age] seconds.
    False means the file is too old (or doesn't exist), True means it is
    up-to-date and valid"""
    if not os.path.isfile(path):
        return False
    cache_modified_time = os.stat(path).st_mtime
    time_now = time.time()
    if cache_modified_time < time_now - max_age:
        # Cache is old
        return False
    else:
        return True

def exists_in_cache(cache_location, url, max_age):
    """Returns if header AND body cache file exist (and are up-to-date)"""
    hpath, bpath = calculate_cache_path(cache_location, url)
    if os.path.exists(hpath) and os.path.exists(bpath):
        return(
            check_cache_time(hpath, max_age)
            and check_cache_time(bpath, max_age)
        )
    else:
        # File does not exist
        return False

def store_in_cache(cache_location, url, response):
    """Tries to store response in cache."""
    hpath, bpath = calculate_cache_path(cache_location, url)
    try:
        outf = open(hpath, "w")
        headers = str(response.info())
        outf.write(headers)
        outf.close()

        outf = open(bpath, "w")
        outf.write(response.read())
        outf.close()
    except IOError:
        return True
    else:
        return False

class CacheHandler(urllib2.BaseHandler):
    """Stores responses in a persistant on-disk cache.

    If a subsequent GET request is made for the same URL, the stored
    response is returned, saving time, resources and bandwidth
    """
    def __init__(self, cache_location, max_age = 21600):
        """The location of the cache directory"""
        self.max_age = max_age
        self.cache_location = cache_location
        if not os.path.exists(self.cache_location):
            os.mkdir(self.cache_location)

    def default_open(self, request):
        """Handles GET requests, if the response is cached it returns it
        """
        if request.get_method() is not "GET":
            return None # let the next handler try to handle the request

        if exists_in_cache(
            self.cache_location, request.get_full_url(), self.max_age
        ):
            return CachedResponse(
                self.cache_location,
                request.get_full_url(),
                set_cache_header = True
            )
        else:
            return None

    def http_response(self, request, response):
        """Gets a HTTP response, if it was a GET request and the status code
        starts with 2 (200 OK etc) it caches it and returns a CachedResponse
        """
        if (request.get_method() == "GET"
            and str(response.code).startswith("2")
        ):
            if 'x-local-cache' not in response.info():
                # Response is not cached
                set_cache_header = store_in_cache(
                    self.cache_location,
                    request.get_full_url(),
                    response
                )
            else:
                set_cache_header = True
            #end if x-cache in response

            return CachedResponse(
                self.cache_location,
                request.get_full_url(),
                set_cache_header = set_cache_header
            )
        else:
            return response

class CachedResponse(StringIO.StringIO):
    """An urllib2.response-like object for cached responses.

    To determine if a response is cached or coming directly from
    the network, check the x-local-cache header rather than the object type.
    """
    def __init__(self, cache_location, url, set_cache_header=True):
        self.cache_location = cache_location
        hpath, bpath = calculate_cache_path(cache_location, url)

        StringIO.StringIO.__init__(self, file(bpath).read())

        self.url     = url
        self.code    = 200
        self.msg     = "OK"
        headerbuf = file(hpath).read()
        if set_cache_header:
            headerbuf += "x-local-cache: %s\r\n" % (bpath)
        self.headers = httplib.HTTPMessage(StringIO.StringIO(headerbuf))

    def info(self):
        """Returns headers
        """
        return self.headers

    def geturl(self):
        """Returns original URL
        """
        return self.url

    def recache(self):
        new_request = urllib2.urlopen(self.url)
        set_cache_header = store_in_cache(
            self.cache_location,
            new_request.url,
            new_request
        )
        CachedResponse.__init__(self, self.cache_location, self.url, True)


if __name__ == "__main__":
    def main():
        """Quick test/example of CacheHandler"""
        opener = urllib2.build_opener(CacheHandler("/tmp/"))
        response = opener.open("http://google.com")
        print response.headers
        print "Response:", response.read()

        response.recache()
        print response.headers
        print "After recache:", response.read()
    main()

Dieser Artikel auf Yahoo Developer Network - http://developer.yahoo.com/ python / python-caching.html -. wird beschrieben, wie http Anrufe über urllib entweder Speicher oder Festplatte gemacht zwischenzuspeichern

@dbr: Sie können auch Caching https Antworten hinzufügen müssen mit:

def https_response(self, request, response):
    return self.http_response(request,response)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top