Zanurkuj w Pythonie/Źródła/openanything.py

u"""OpenAnything: mała biblioteka dla HTTP

Ten program jest częścią książki "Zanurkuj w Pythonie", podręcznika
o Pythonie dla doświadczonych programistów. Najnowszą wersję można
znaleźć tu: http://pl.wikibooks.org/wiki/Zanurkuj_w_Pythonie.
 
Program ten został oparty na przykładach zawartych w książce
"Dive Into Python", a dostępnej stąd: http://www.diveintopython.org.
"""

__author__ = 'Mark Pilgrim (mark@diveintopython.org)'
__version__ = '$Revision: 1.6 $'[11:-2]
__date__ = '$Date: 2004/04/16 21:16:24 $'
__copyright__ = 'Copyright (c) 2004 Mark Pilgrim'
__license__ = 'Python'

import urllib2, urlparse, gzip
from StringIO import StringIO

USER_AGENT = 'OpenAnything/%s +http://diveintopython.org/http_web_services/' % __version__

class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
    def http_error_301(self, req, fp, code, msg, headers):
        result = urllib2.HTTPRedirectHandler.http_error_301(
            self, req, fp, code, msg, headers)
        result.status = code
        return result

    def http_error_302(self, req, fp, code, msg, headers):
        result = urllib2.HTTPRedirectHandler.http_error_302(
            self, req, fp, code, msg, headers)
        result.status = code
        return result

class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler):
    def http_error_default(self, req, fp, code, msg, headers):
        result = urllib2.HTTPError(
            req.get_full_url(), code, msg, headers, fp)
        result.status = code
        return result

def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
    u"""URL, nazwa pliku lub łańcuch znaków --> strumień

    Funkcja ta pozwala tworzyć parsery, które przyjmują jakieś źródło wejścia
    (URL, ścieżkę do pliku lokalnego lub gdzieś w sieci lub dane w postaci łańcucha znaków),
    a następnie zaznajamia się z nim w odpowiedni sposób. Zwracany obiekt będzie
    posiadał wszystkie podstawowe metody czytania wejścia (read, readline, readlines).
    Ponadto korzystamy z .close(), gdy obiekt już nam nie będzie potrzebny.

    Kiedy zostanie podany argument etag, zostanie on wykorzystany jako wartość
    nagłówka żądania URL-a If-None-Match.

    Jeśli argument lastmodified zostanie podany, musi być on formie
    łańcucha znaków określającego czas i datę w GMT.
    Data i czas sformatowana w tym łańcuchu zostanie wykorzystana
    jako wartość nagłówka żądania If-Modified-Since.

    Jeśli argument agent zostanie określony, będzie on wykorzystany
    w nagłówku żądania User-Agent.
    """

    if hasattr(source, 'read'):
        return source

    if source == '-':
        return sys.stdin

    if urlparse.urlparse(source)[0] == 'http':
        # otwiera URL za pomocą urllib2
        request = urllib2.Request(source)
        request.add_header('User-Agent', agent)
        if lastmodified:
            request.add_header('If-Modified-Since', lastmodified)
        if etag:
            request.add_header('If-None-Match', etag)
        request.add_header('Accept-encoding', 'gzip')
        opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler())
        return opener.open(request)
    
    # próbuje otworzyć za pomocą wbudowanej funkcji open (jeśli source to nazwa pliku)
    try:
        return open(source)
    except (IOError, OSError):
        pass

    # traktuje source jak łańcuch znaków
    return StringIO(str(source))

def fetch(source, etag=None, lastmodified=None, agent=USER_AGENT):
    u"""Pobiera dane z URL, pliku, strumienia lub łańcucha znaków"""
    result = {}
    f = openAnything(source, etag, lastmodified, agent)
    result['data'] = f.read()
    if hasattr(f, 'headers'):
        # zapisuje ETag, jeśli go wysłał do nas serwer
        result['etag'] = f.headers.get('ETag')
        # zapisuje nagłówek Last-Modified, jeśli został do nas wysłany
        result['lastmodified'] = f.headers.get('Last-Modified')
        if f.headers.get('content-encoding') == 'gzip':
            # odkompresowuje otrzymane dane, ponieważ są one zakompresowane jako gzip
            result['data'] = gzip.GzipFile(fileobj=StringIO(result['data'])).read()
    if hasattr(f, 'url'):
        result['url'] = f.url
        result['status'] = 200
    if hasattr(f, 'status'):
        result['status'] = f.status
    f.close()
    return result