Zanurkuj w Pythonie/HTTP - wszystko razem

Z Wikibooks, biblioteki wolnych podręczników.

Python/HTTP - wszystko razem[edytuj]

Widzieliśmy już wszystkie elementy potrzebne do utworzenia inteligentnego klienta usługi internetowej. Teraz zobaczmy jak to wszystko do siebie pasuje.

Przykład 11.17. Funkcja openanything

Ta funkcja jest zdefiniowana w pliku openanything.py.

def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):
    # non-HTTP code omitted for brevity
    if urlparse.urlparse(source)[0] == 'http':                                       #(1)
        # open URL with urllib2
        request = urllib2.Request(source)
        request.add_header('User-Agent', agent)                                      #(2)
        if etag:
            request.add_header('If-None-Match', etag)                                #(3)
        if lastmodified:
            request.add_header('If-Modified-Since', lastmodified)                    #(4)
        request.add_header('Accept-encoding', 'gzip')                                #(5)
        opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler()) #(6)
        return opener.open(request)                                                  #(7)
  1. urlparse to bardzo poręczny moduł do, na pewno zgadłeś, parsowania URL-i. Jego podstawowa funkcja, także nazywająca się urlparse, przyjmuje na wejściu URL-a i dzieli go na taką krotkę (schemat, domena, ścieżka, parametry, parametry w żądaniu i identyfikator fragmentu). Jednyną wsród tych rzeczy jaką musimy się przejmować jest schemat, który decyduje o tym czy mamy do czynienie z URL-em HTTP (który to moduł urllib2 może obsłużyć).
  2. Przedstawiamy się serwerowi HTTP przy pomocy nagłówka User-Agent przesłanego przez funkcję wywołującą. Jeśli nie zostałaby podana wartość User-Agent, użylibysmy wcześniej zdefiniowanej wartośći w openanything.py. Nigdy nie należy używać domyślnej wartości zdefiniowanej w urllib2.
  3. Jeśli została podana suma kontrolna dla ETag, wysyłamy ją w nagłówku If-None-Match.
  4. Jeśli została podana data ostatniej modyfikacji, wysyłamy ją w nagłówku If-Modified-Since.
  5. Powiadamiamy serwer, że chcemy dane skompresowane, jeśli to jest tylko możliwe.
  6. Wywołujemy funkcję build_opener, która wykorzystuje nasze własne klasy obsługi URL-i: SmartRedirectHandler do obsługi przekierowań 301 i 302 i DefaultErrorHandler do taktownej obsługi 304, 404, i innych błędnych sytuacji.
  7. I to wszystko! Otwieramy URL-a i zwracamy plikopodobny (ang. file-like) obiekt do funkcji wywołującej.

Przykład 11.18. Funkcja fetch

Ta funkcja jest zdefiniowana w pliku openanything.py.

def fetch(source, etag=None, last_modified=None, agent=USER_AGENT):
    '''Fetch data and metadata from a URL, file, stream, or string'''
    result = {}
    f = openAnything(source, etag, last_modified, agent)              #(1)
    result['data'] = f.read()                                         #(2)
    if hasattr(f, 'headers'):
        # save ETag, if the server sent one
        result['etag'] = f.headers.get('ETag')                        #(3)
        # save Last-Modified header, if the server sent one
        result['lastmodified'] = f.headers.get('Last-Modified')       #(4)
        if f.headers.get('content-encoding', '') == 'gzip':           #(5)
            # data came back gzip-compressed, decompress it
            result['data'] = gzip.GzipFile(fileobj=StringIO(result['data']])).read()
    if hasattr(f, 'url'):                                             #(6)
        result['url'] = f.url
        result['status'] = 200
    if hasattr(f, 'status'):                                          #(7)
        result['status'] = f.status
    f.close()
    return result
  1. Po pierwsze wywołujemy funkcję openAnything z URL-em, sumą kontrolną ETag, datą ostatniej modyfikacji (ang. Last-Modified date) i wartością User-Agent.
  2. Czytamy aktualne dane zwrócone przez serwer. Mogą one być spakowane; jeśli tak, to później je rozpakowujemy.
  3. Zachowujemy sumę kontrolną ETag zwróconą przez serwer; aplikacja wywołująca może ją przesłać następnym razem, przekazując ją dalej do openAnything, która umieści ją w nagłówku If-None-Match i przesłać do zdalnego serwera.
  4. Zachowujemy także datę ostatniej modyfikacji.
  5. Jeśli serwer powiedział, że wysłał spakowane dane, rozpakowujemy je.
  6. Jeśli dostaliśmy URL-a z powrotem od serwera, zachowujemy go i zakładamy, że kod statusu wynosi 200, dopóki nie przekonamy się, że jest inaczej.
  7. Jeśli któraś z naszych klas obsługi URL-i przechwyci jakiś kod statusu, zachowujemy go także.

Przykład 11.19. Użycie openanything.py

>>> import openanything
>>> useragent = 'MyHTTPWebServicesApp/1.0'
>>> url = 'http://diveintopython.org/redir/example301.xml'
>>> params = openanything.fetch(url, agent=useragent)              #(1)
>>> params                                                         #(2)
{'url': 'http://diveintomark.org/xml/atom.xml', 
'lastmodified': 'Thu, 15 Apr 2004 19:45:21 GMT', 
'etag': '"e842a-3e53-55d97640"', 
'status': 301,
'data': '<?xml version="1.0" encoding="iso-8859-1"?>
<feed version="0.3"
<-- rest of data omitted for brevity -->'}
>>> if params['status'] == 301:                                    #(3)
...     url = params['url']
>>> newparams = openanything.fetch(
...     url, params['etag'], params['lastmodified'], useragent)    #(4)
>>> newparams
{'url': 'http://diveintomark.org/xml/atom.xml', 
'lastmodified': None, 
'etag': '"e842a-3e53-55d97640"', 
'status': 304,
'data': }                                                        #(5)
  1. Za pierwszym razem, gdy pobieramy jakiś zasób, nie mamy żadnej sumy kontrolnej ETag ani daty ostatniej modyfikacji, a więc opuszczamy te parametry. (To są parametry opcjonalne.)
  2. Z powrotem otrzymujemy słownik kilku użytecznych nagłówków, kod statusu HTTP i aktualne dane zwrócone przez serwer. Funkcja openanything zajmuje się samodzielnie rozpakowaniem archiwum gzip; nie zajmujemy się tym na tym poziomie.
  3. Jeśli kiedykolwiek otrzymamy kod statusu 301, czyli trwałe przekierowanie, to musimy zaktualizować naszego URL-a na nowy adres.
  4. Gdy po raz drugi pobieramy ten sam zasób, to mamy wiele informacji, które możemy przekazać: (być może zaktualizowany) URL, ETag z ostatniego razu, data ostatniej modyfikacji i oczywiście nasz User-Agent.
  5. Z powrotem ponownie otrzymujemy słownik, ale dane nie uległy zmianie, a więc wszystko co dostaliśmy to był kod statusu 304 i żadnych danych.