Zanurkuj w Pythonie/Korzystanie z Last-Modified i ETag
Korzystanie z Last-Modified i ETag
edytujTeraz gdy już wiesz jak dodawać własne nagłówki do swoich żądań HTTP, zobaczmy jak wykorzystać nagłówki Last-Modified i ETag.
Poniższe przykłady pokazują wyjście z wyłączonym debugowaniem. Jeśli nadal masz je włączone (tak jak w poprzedniej sekcji), możesz je wyłączyć poprzez takie ustawienie: opener.handle_open['http'][0].set_http_debuglevel(0)
odwołujące się pośrednio do instancji 'handler' klasy 'urllib2.HTTPHandler', przez słownik w instancji 'opener' lub bezpośrednio handler.set_http_debuglevel(0)
.
Albo możesz pozostawić debugowanie włączone, jeśli to Ci pomoże.
>>> import urllib2 >>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml') >>> opener = urllib2.build_opener() >>> firstdatastream = opener.open(request) >>> firstdatastream.headers.dict #(1) {'date': 'Thu, 15 Apr 2004 20:42:41 GMT', 'server': 'Apache/2.0.49 (Debian GNU/Linux)', 'content-type': 'application/atom+xml', 'last-modified': 'Thu, 15 Apr 2004 19:45:21 GMT', 'etag': '"e842a-3e53-55d97640"', 'content-length': '15955', 'accept-ranges': 'bytes', 'connection': 'close'} >>> request.add_header('If-Modified-Since', ... firstdatastream.headers.get('Last-Modified')) #(2) >>> seconddatastream = opener.open(request) #(3) Traceback (most recent call last): File "<stdin>", line 1, in ? File "c:\python23\lib\urllib2.py", line 326, in open '_open', req) File "c:\python23\lib\urllib2.py", line 306, in _call_chain result = func(*args) File "c:\python23\lib\urllib2.py", line 901, in http_open return self.do_open(httplib.HTTP, req) File "c:\python23\lib\urllib2.py", line 895, in do_open return self.parent.error('http', req, fp, code, msg, hdrs) File "c:\python23\lib\urllib2.py", line 352, in error return self._call_chain(*args) File "c:\python23\lib\urllib2.py", line 306, in _call_chain result = func(*args) File "c:\python23\lib\urllib2.py", line 412, in http_error_default raise HTTPError(req.get_full_url(), code, msg, hdrs, fp) urllib2.HTTPError: HTTP Error 304: Not Modified
- Pamiętasz wszystkie te nagłówki HTTP, które były wyświetlane przy włączonym debugowaniu? To jest sposób na dotarcie do nich programowo:
firstdatastream.headers
jest obiektem działającym jak słownik i pozwala on na pobranie każdego nagłówka zwracanego przez serwer HTTP. - Przy drugim żądaniu dodajemy nagłówek If-Modified-Since z datą ostatniej modyfikacji z pierwszego żądania. Jeśli data nie uległa zmianie, serwer powinien zwrócić kod statusu 304.
- To wystarczy do stwierdzenia, że dane nie uległy zmianie. Możemy zobaczyć w zrzucie błędów, że urllib2 rzucił wyjątek specjalny:
HTTPError
w odpowiedzi na kod statusu 304. To jest trochę niezwykłe i nie całkiem pomocne. W końcu to nie jest błąd; specjalnie poprosiliśmy serwer o nie przesyłanie żadnych danych, jeśli nie uległy one zmianie i dane nie uległy zmianie, a więc serwer powiedział, iż nie wysłał żadnych danych. To nie jest błąd; to jest dokładnie to czego oczekiwaliśmy.
urllib2
rzuca wyjątek HTTPError
także w sytuacjach, które można traktować jak błędy, np. 404 (strona nieznaleziona). Właściwie to rzuca on wyjątek HTTPError
dla każdego kodu statusu innego niż 200 (OK), 301 (stałe przekierowanie), lub 302 (tymczasowe przekierowanie). Do naszych celów byłoby przydatne przechwycenie tych kodów statusów i po prostu zwrócenie ich bez rzucania żadnych wyjątków. Aby to zrobić, musimy zdefiniować własną klasę obsługi URL-i (ang. URL handler).
Poniższa klasa obsługi URL-i jest częścią modułu openanything.py.
class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler): #(1)
def http_error_default(self, req, fp, code, msg, headers): #(2)
result = urllib2.HTTPError(
req.get_full_url(), code, msg, headers, fp)
result.status = code #(3)
return result
urllib2
jest zaprojektowana jako zbiór klas obsługi URL-i. Każda z tych klas może definiować dowolną liczbę metod. Gdy coś się wydarzy -- jak np. błąd HTTP lub nawet kod 304 --urllib2
używa introspekcji do odnalezienia w liście zdefiniowanych klas obsługi URL-i metody, która może obsłużyć to zdarzenie. Używaliśmy podobnej introspekcji w rozdziale 9-tym, Przetwarzanie XML-a do zdefiniowania metod obsługi dla różnych typów węzłów, aleurllib2
jest bardziej elastyczny i przeszukuje tyle klas obsługi ile jest zdefiniowanych dla bieżącego żądania.urllib2
przeszukuje zdefiniowane klasy obsługi i wywołuje metodęhttp_error_default
, gdy otrzyma kod statusu 304 od serwera. Definiując własną klasę obsługi błędów, możemy zapobiec rzucaniu wyjątków przezurllib2
. Zamiast tego tworzymy obiektHTTPError
, ale zwracamy go, zamiast rzucania go jako wyjątek.- To jest kluczowa część: przed zwróceniem zachowujemy kod statusu zwrócony przez serwer HTTP. To pozwala na dostęp do niego programowi wywołującemu.
>>> request.headers #(1) {'If-modified-since': 'Thu, 15 Apr 2004 19:45:21 GMT'} >>> import openanything >>> opener = urllib2.build_opener( ... openanything.DefaultErrorHandler()) #(2) >>> seconddatastream = opener.open(request) >>> seconddatastream.status #(3) 304 >>> seconddatastream.read() #(4) ''
- Kontynuujemy poprzedni przykład, a więc obiekt Request jest już utworzony i nagłówek If-Modified-Since został już dodany.
- To jest klucz: ponieważ mamy zdefiniowaną własną klasę obsługi URL-i, musimy powiedzieć
urllib2
, aby teraz jej używał. Pamiętasz jak mówiłem, iżurllib2
podzielił proces dostępu do zasobów HTTP na trzy etapy i to nie bez powodu? Oto dlaczego wywołanie funkcjibuild_opener
jest odrębnym etapem. Ponieważ na jej wejściu możesz podać własne klasy obsługi URL-i, które powodują zmianę domyślnego działaniaurllib2
. - Teraz możemy zasób otworzyć po cichu a z powrotem otrzymujemy obiekt, który wraz z nagłówkami (użyj
seconddatastream.headers.dict
, aby je pobrać), zawiera także kod statusu HTTP. W tym wypadku, tak jak oczekiwaliśmy, tym statusem jest 304, który oznacza że dane nie zmieniły się od ostatniego razu, gdy o nie prosiliśmy. - Zauważ, że gdy serwer odsyła kod statusu 304, to nie przesyła ponownie danych. I o to w tym wszystkim chodzi: aby oszczędzić przepustowość poprzez niepobieranie ponowne danych, które nie uległy zmianie. A więc jeśli potrzebujesz tych danych, to musisz zachować je w lokalnym buforze po pierwszym pobraniu.
Z nagłówka ETag korzystamy bardzo podobnie. Jednak zamiast sprawdzania nagłówka Last-Modified i przesyłania If-Modified-Since, sprawdzamy nagłówek ETag a przesyłamy If-None-Match. Zacznijmy całkiem nową sesję w naszym IDE.
>>> import urllib2, openanything >>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml') >>> opener = urllib2.build_opener( ... openanything.DefaultErrorHandler()) >>> firstdatastream = opener.open(request) >>> firstdatastream.headers.get('ETag') #(1) '"e842a-3e53-55d97640"' >>> firstdata = firstdatastream.read() >>> print firstdata #(2) <?xml version="1.0" encoding="iso-8859-1"?> <feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="en"> <title mode="escaped">dive into mark</title> <link rel="alternate" type="text/html" href="http://diveintomark.org/"/> [.. ciach ..] >>> request.add_header('If-None-Match', ... firstdatastream.headers.get('ETag')) #(3) >>> seconddatastream = opener.open(request) >>> seconddatastream.status #(4) 304 >>> seconddatastream.read() #(5) ''
- Używając pseudo-słownika
firstdatastream.headers
możemy pobrać nagłówek ETag zwrócony przez serwer. (Co się stanie, jeśli serwer nie zwróci nagłówka ETag? Wtedy ta linia powinna zwrócićNone
.) - OK, mamy dane.
- Teraz przy drugim wywołaniu ustawiamy w nagłówku If-None-Match wartość sumy kontrolnej z ETag otrzymanego przy pierwszym wywołaniu.
- Drugie wywołanie działa prawidłowo bez żadnych zakłóceń (bez rzucania żadnych wyjątków) i ponownie widzimy, że serwer odesłał status kodu 304. Bazując na sumie kontrolnej nagłówka ETag, którą wysłaliśmy za drugim razem, wie on że dane nie zmieniły się.
- Niezależnie od tego, czy kod 304 jest rezultatem sprawdzania daty Last-Modified czy sumy kontrolnej ETag, nigdy nie otrzymamy z powrotem ponownie tych samych danych, a jedynie kod statusu 304. I o to chodziło.
W tych przykładach serwer HTTP obsługiwał zarówno nagłówek Last-Modified jak i ETag, ale nie wszystkie serwery to potrafią. Jako użytkownik serwisów internetowych powinieneś być przygotowany do używania ich obu, ale musisz programować defensywnie na wypadek gdyby serwer obsługiwał tylko jeden z tych nagłówków lub żaden. |