Zanurkuj w Pythonie/Właściwości HTTP

Możemy wyróżnić pięć ważnych właściwości HTTP, z których powinniśmy korzystać.

User-Agent edytuj

User-Agent jest to po prostu sposób w jaki klient może poinformować serwer kim jest w trakcie żądania strony internetowej, RSS (Really Simple Syndication) lub jakiejkolwiek usługi internetowej poprzez HTTP. Gdy klient zgłasza żądanie do danego zasobu, to zawsze powinien informować kim jest tak szczegółowo, jak to tylko możliwe. Pozwala to administratorowi serwera na skontaktowanie się z programistą, twórcą aplikacji klienckiej, jeśli coś idzie nie tak.

Domyślnie Python wysyła standardowy nagłówek postaci User-Agent: Python-urllib/1.15. W następnej sekcji zobaczymy jak to zmienić na coś bardziej szczegółowego.

Przekierowania edytuj

Czasami zasoby zmieniają położenie. Witryny internetowe są reorganizowane a strony przenoszone pod nowe adresy. Nawet usługi internetowe ulegają reorganizacji. RSS spod adresu http://example.com/index.xml może zostać przeniesiony na http://example.com/xml/atom.xml. Może się także zmienić cała domena, gdy reorganizacja jest przeprowadzana na większą skalę, np. http://www.example.com/index.xml może zostać przekierowana do http://server-farm-1.example.com/index.xml.

Zawsze gdy żądamy jakiegoś zasobu od serwera HTTP, serwer ten dołącza do swojej odpowiedzi kod statusu. Kod statusu 200 oznacza "wszystko w porządku, oto strona o którą prosiliśmy". Kod statusu 404 oznacza "strona nieznaleziona". (Prawdopodobnie spotkaliśmy się z błędami 404 podczas surfowania w sieci.)

Protokół HTTP ma dwa różne sposoby, aby dać do zrozumienia, że dany zasób zmienił adres. Kod statusu 302 jest to tymczasowe przekierowanie i oznacza on "ups, to zostało tymczasowo przeniesione tam" (a ten tymczasowy adres umieszczany jest w nagłówku Location:). Kod statusu 301 jest to przekierowanie trwałe i oznacza "ups, to zostało przeniesione na stałe" (a nowy adres jest podawany w nagłówku Location:). Gdy otrzymamy kod statusu 302 i nowy adres, to specyfikacja HTTP mówi, że powinniśmy użyć nowego adresu, aby pobrać to czego dotyczyło żądanie, ale następnym razem przy próbie dostępu do tego samego zasobu powinniśmy spróbować ponownie starego adresu. Natomiast gdy dostaniemy kod statusu 301 i nowy adres, to powinniśmy już od tego momentu używać tylko tego nowego adresu.

urllib.urlopen automatycznie "śledzi" przekierowania, jeśli otrzyma stosowny kod statusu od serwera HTTP, ale niestety nie informuje o tym fakcie. Ostatecznie otrzymujemy dane, o które prosiliśmy, ale nigdy nie wiadomo, czy biblioteka nie podążyła samodzielnie za przekierowaniem. Tak więc nieświadom niczego dalej możemy próbować korzystać ze starego adresu i za każdym razem nastąpi przekierowanie pod nowy adres. To powoduje wydłużenie drogi, co nie jest zbyt wydajne! W dalszej części tego rozdziału zobaczymy, jak sobie radzić z trwałymi przekierowaniami właściwie i wydajnie.

Last-Modified/If-Modified-Since edytuj

Niektóre dane zmieniają się bez przerwy. Strona domowa cnn.com jest aktualizowana co pięć minut. Z drugiej strony strona domowa google.com zmienia się raz na kilka tygodni (gdy wrzucają jakieś świąteczne logo lub reklamują jakąś nową usługę). Usługi internetowe nie różnią się pod tym względem. Serwer zwykle wie kiedy dane, które pobieramy się ostatnio zmieniły, a protokół HTTP pozwala serwerowi na dołączenie tej daty ostatniej modyfikacji do żądanych danych.

Gdy poprosimy o te same dane po raz drugi (lub trzeci, lub czwarty), możemy podać serwerowi datę ostatniej modyfikacji (ang. last-modified date), którą dostaliśmy poprzednio. Wysyłamy serwerowi nagłówek If-Modified-Since wraz ze swoim żądaniem oraz datą otrzymaną od serwera ostatnim razem. Jeśli dane nie uległy zmianie od tamtego czasu, to serwer odsyła specjalny kod statusu 304, który oznacza "dane nie zmieniły się od czasu, gdy o nie ostatnio pytałeś". Dlaczego jest to lepsze rozwiązanie? Bo gdy serwer odsyła kod 304, to nie wysyła ponownie danych. Wszystko co otrzymujemy to kod statusu. Tak więc nie musimy ciągle pobierać tych samych danych w kółko, jeśli nie uległy zmianie. Serwer zakłada, że już mamy te dane zachowane gdzieś lokalnie.

Wszystkie nowoczesne przeglądarki internetowe wspierają sprawdzanie daty ostatniej modyfikacji. Być może kiedyś odwiedziliśmy jakąś stronę jednego dnia, a potem odwiedziliśmy ją ponownie następnego i zauważyliśmy, że nie uległa ona zmianie, a jednocześnie zadziwiająco szybko się załadowała. Przeglądarka zachowała zawartość tej strony w lokalnym buforze podczas pierwszej wizyty, a podczas drugiej automatycznie wysłała datę ostatniej modyfikacji otrzymaną za pierwszym razem. Serwer po prostu odpowiedział kodem 304: Not Modified, a więc przeglądarka wiedziała, że może załadować stronę z lokalnego bufora. Usługi internetowe mogą być również takie sprytne.

Biblioteka URL Pythona nie ma wbudowanego wsparcia dla kontroli daty ostatniej modyfikacji, ale ponieważ możemy dodawać dowolne nagłówki do każdego żądania i czytać dowolne nagłówki z każdej odpowiedzi, to możemy dodać taką kontrolę samodzielnie.

ETag/If-None-Match edytuj

Znaczniki ETag są alternatywnym sposobem na osiągnięcie tego samego celu, co poprzez kontrolę daty ostatniej modyfikacji: nie pobieramy ponownie danych, które się nie zmieniły. A działa to tak: serwer wysyła jakąś sumę kontrolną danych (w nagłówku ETag) razem z żądanymi danymi. Jak ta suma kontrolna jest ustalana, to zależy wyłącznie od serwera. Gdy po raz drugi chcemy pobrać te same dane, dołączamy sumę kontrolną z nagłówka ETag w nagłówku If-None-Match: i jeśli dane się nie zmieniły serwer odeśle kod statusu 304. Tak jak w przypadku kontroli daty ostatniej modyfikacji, serwer odsyła tylko kod 304 - nie wysyła po raz drugi tych samych danych. Poprzez dołączenie sumy kontrolnej ETag do drugiego żądania mówimy serwerowi, iż nie ma potrzeby, aby wysyłał po raz drugi tych samych danych, jeśli nadal odpowiadają one tej sumie kontrolnej, ponieważ cały czas mamy dane pobrane ostatnio.

Biblioteka URL Pythona nie ma wbudowanego wsparcia dla znaczników ETag, ale zobaczymy, jak można je dodać w dalszej części tego rozdziału.

Kompresja edytuj

Ostatnią istotną właściwością HTTP, którą możemy ustawić, jest kompresja gzip. Gdy mówimy o usługach sieciowych za pośrednictwem HTTP, to zwykle mówimy o przesyłaniu XML tam i z powrotem. XML jest tekstem i to zwykle całkiem rozwlekłym tekstem, a tekst dobrze się kompresuje. Gdy żądamy jakiegoś zasobu poprzez HTTP, to możemy poprosić serwer, jeśli ma jakieś nowe dane do wysłania, aby wysłał je w formie skompresowanej. Dołączamy wtedy nagłówek Accept-encoding: gzip do żądania i jeśli serwer wspiera kompresję, odeśle on dane skompresowane w formacie gzip i oznaczy je nagłówkiem Content-encoding: gzip.

Biblioteka URL Pythona nie ma wbudowanego wsparcia dla kompresji gzip jako takiej, ale możemy dodawać dowolne nagłówki do żądania, a Python posiada oddzielny moduł gzip zawierający funkcje, których można użyć do dekompresji danych samodzielnie.

Zauważmy, że nasz jednolinijkowy skrypt pobierający RSS nie uwzględnia żadnej z tych właściwości HTTP. Zobaczmy, jak możemy go udoskonalić.