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