12. Usługi internetowe w języku Python
![]() |
Skrypty w języku Python mogą być wykonywane przez serwer WEB. To właśnie serwer ten będzie nasłuchiwał żądań od klientów. Z punktu widzenia klienta wywołanie usługi WEB sprowadza się do zażądania URL tej usługi. Klient może być napisany w dowolnym języku, w szczególności w Pythonie. Musimy umieć „komunikować się” z usługą WEB, czyli rozumieć protokół komunikacji Http między serwerem WWW a jego klientami. Taki jest cel poniższych programów.
Skrypty serwisu internetowego będą uruchamiane przez serwer WWW Apache o nazwie WampServer. Należy je umieścić w specjalnym katalogu: <WampServer>\bin\apache\apachex.y.z\cgi-bin, gdzie <WampServer> to katalog instalacyjny WampServer, a x.y.z to wersja serwera WWW Apache.
![]() |
Umieszczenie skryptów w języku Python w folderze <cgi-bin> nie wystarczy. W pierwszym wierszu skryptu należy podać ścieżkę do interpretera języka Python, który ma być używany. Ścieżka ta jest podana w formie komentarza:
Czytelnik powinien dostosować tę ścieżkę do swojego środowiska.
12.1. Aplikacja klient-serwer do obsługi daty i godziny
Naszą pierwszą usługą internetową będzie usługa daty i godziny: klient otrzymuje bieżącą datę i godzinę.
12.1.1. Serwer
#!D:\Programy\ActivePython\Python2.7.2\python.exe
import time
# nagłówki
print "Content-Type: text/plain\n"
# wysyłanie godziny do klienta
# czas lokalny: liczba milisekund od 01.01.1970
# „format wyświetlania daty i godziny”
# d: dzień (2 cyfry)
# m: miesiąc (2 cyfry)
# y: rok (2 cyfry)
# H: godzina 0,23
# M: minuty
# S: sekundy
print time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
Uwagi:
- wiersz 6: skrypt musi samodzielnie wygenerować niektóre nagłówki HTTP odpowiedzi dla klienta. Zostaną one dodane do nagłówków HTTP wygenerowanych przez sam serwer Apache. Nagłówek HTTP w wierszu 6 informuje klienta, że zostanie mu wysłany zasób w formacie text/plain, czyli tekst nieformatowany. Należy zwrócić uwagę na znak „\n” na końcu nagłówka, który spowoduje wygenerowanie pustej linii po nagłówku. Jest to obowiązkowe: to właśnie ta pusta linia sygnalizuje klientowi HTTP koniec nagłówków HTTP w odpowiedzi. Następnie pojawia się zasób żądany przez klienta, w tym przypadku tekst nieformatowany;
- wiersz 18: zasób wysłany do klienta to tekst wyświetlający bieżącą datę i godzinę.
12.1.2. Dwa testy
Powyższy skrypt można uruchomić bezpośrednio w interpreterze języka Python w oknie poleceń, tak jak robiliśmy to do tej pory. Pozwala to wyeliminować ewentualne błędy składniowe lub operacyjne. Otrzymujemy następujący wynik:
Po przetestowaniu skryptu w ten sposób można umieścić go w folderze <cgi-bin> na serwerze Apache (patrz akapit 12). Uruchommy aplikację WampServer. Spowoduje to uruchomienie zarówno serwera WWW Apache, jak i systemu zarządzania bazami danych MySQL. Na razie będziemy korzystać wyłącznie z serwera WWW. Następnie w przeglądarce wpiszmy następujący adres: http://localhost/cgi-bin/web_02.py:
![]() |
- w [1]: żądany plik URL;
- na [2]: odpowiedź wyświetlona przez przeglądarkę;
- w [3]: kod źródłowy odebrany przez przeglądarkę internetową. Jest to rzeczywiście ten sam kod, który został wysłany przez skrypt w języku Python.
Za pomocą niektórych narzędzi (w tym przypadku Firebug, wtyczka do przeglądarki Firefox) można uzyskać dostęp do nagłówków HTTP wymienianych z serwerem. Powyżej przeglądarka internetowa otrzymała następujące nagłówki HTTP:
W wierszach 7–8 rozpoznajemy nagłówek HTTP wysłany przez skrypt w języku Python. Poprzedzające go nagłówki zostały wygenerowane przez serwer WWW Apache.
12.1.3. Zaprogramowany klient
Teraz napiszemy skrypt, który będzie klientem poprzedniej usługi internetowej. Wykorzystamy funkcje modułu httplib, który ułatwia pisanie klientów HTTP.
# -*- coding=utf-8 -*-
import httplib,re
# stałe
HOST="localhost"
URL="/cgi-bin/web_02.py"
# połączenie
connexion=httplib.HTTPConnection(HOST)
# śledzenie
connexion.set_debuglevel(1)
# wysyłanie zapytania
connexion.request("GET", URL)
# przetwarzanie odpowiedzi
reponse=connexion.getresponse()
# treść
contenu=reponse.read()
# zamknięcie połączenia
connexion.close()
print "------\n",contenu,"-----\n"
# pobieranie elementów czasu
elements=re.match(r"^(\d\d)/(\d\d)/(\d\d) (\d\d):(\d\d):(\d\d)\s*$",contenu).groups()
print "Jour=%s,Mois=%s,An=%s,Heures=%s,Minutes=%s,Secondes=%s" % (elements[0],elements[1],elements[2],elements[3],elements[4],elements[5])
Uwagi:
- wiersz 3: moduł re jest niezbędny do obsługi wyrażeń regularnych, a moduł httplib do obsługi funkcji klientów HTTP;
- wiersz 9: tworzone jest połączenie HTTP z portem 80 modułu HOST zdefiniowanego w wierszu 6;
- wiersz 11: monitorowanie pozwala wyświetlić nagłówki HTTP żądania klienta oraz odpowiedzi serwera;
- wiersz 13: wysyłane jest żądanie do serwisu internetowego URL. Istnieją dwa sposoby jej wywołania: za pomocą polecenia HTTP GET lub POST. Różnica między nimi została wyjaśniona poniżej. W tym przypadku zostanie ona wywołana za pomocą polecenia HTTP GET;
- wiersz 15: odczytywana jest odpowiedź serwera. Otrzymujemy tutaj całą odpowiedź: nagłówki HTTP oraz zasób żądany przez klienta. W swojej odpowiedzi serwer mógł poprosić klienta o przekierowanie. W takim przypadku klient httplib automatycznie dokonuje przekierowania. Otrzymana odpowiedź jest zatem wynikiem tego przekierowania;
- wiersz 17: odpowiedź składa się z nagłówków HTTP oraz dokumentu żądanego przez klienta. Aby uzyskać wyłącznie nagłówki HTTP, należy użyć funkcji [reponse].getHeaders(). Aby uzyskać dokument, należy użyć funkcji [reponse].read();
- wiersz 19: po otrzymaniu odpowiedzi z serwera WWW połączenie z nim zostaje zamknięte;
- wiemy, że dokument wysłany przez serwer to wiersz tekstu w postaci 15/06/11 14:56:36. W wierszach 22–26 używamy wyrażenia regularnego, aby wyodrębnić poszczególne elementy tego wiersza.
12.1.4. Wyniki
Uwagi:
- wiersz 1: nagłówki HTTP wysłane przez klienta do serwera WWW;
- wiersze 2–6: nagłówki HTTP z odpowiedzi serwera WWW;
- wiersz 8: dokument wysłany przez serwer;
- wiersz 11: wynik jego przetworzenia;
12.2. Pobieranie przez serwer parametrów wysłanych przez klienta
W protokole HTTP klient ma dwie metody przekazywania parametrów do serwera WWW:
- wysyła żądanie usługi URL w postaci
GET url?param1=val1¶m2=val2¶m3=val3… HTTP/1.0
gdzie wartości vali muszą zostać wcześniej zakodowane, tak aby niektóre znaki zarezerwowane zostały zastąpione ich wartościami szesnastkowymi.
- żąda kodu URL usługi w postaci
a następnie wśród nagłówków HTTP wysyłanych do serwera umieszcza następujący nagłówek:
Kolejne nagłówki wysyłane przez klienta kończą się pustym wierszem. Następnie może wysłać swoje dane w postaci
gdzie ciągi vali muszą, podobnie jak w przypadku metody GET, zostać wcześniej zakodowane. Liczba znaków wysyłanych do serwera musi wynosić N, gdzie N jest wartością zadeklarowaną w nagłówku:
12.2.1. Usługa internetowa
Poniższa usługa internetowa otrzymuje od klienta trzy parametry: nom, prenom, age. Pobiera je z pewnego rodzaju słownika o nazwie cgi.FieldStorage, dostarczonego przez moduł cgi. Wartość vali parametru parami uzyskuje się za pomocą funkcji vali=cgi.FieldStorage().getlist("parami"). Otrzymujemy tablicę zawierającą:
- 0 elementów, jeśli parametr parami nie występuje w żądaniu klienta;
- 1 element, jeśli parametr parami występuje 1 raz w żądaniu klienta;
- n elementów, jeśli parametr parami występuje n razy w żądaniu klienta.
Po pobraniu parametrów skrypt zwraca je klientowi.
#!D:\Programs\ActivePython\Python2.7.2\python.exe
import cgi
# nagłówki
print "Content-Type: text/plain\n"
# odzyskiwanie przez serwer informacji wysłanych przez klienta
# tutaj imię=P&nazwisko=N&wiek=A
formulaire=cgi.FieldStorage()
# są one odsyłane do klienta
print "informations recues du service web [prenom=%s,nom=%s,age=%s]" % (formulaire.getlist("prenom"),formulaire.getlist("nom"),formulaire.getlist("age"))
Test można przeprowadzić za pomocą przeglądarki internetowej:
![]() |
W [1] znajduje się URL z serwisu internetowego. Należy zwrócić uwagę na obecność trzech parametrów: nom, prenom, age. W [2] znajduje się odpowiedź serwisu internetowego.
12.2.2. Klient GET
# -*- coding=utf-8 -*-
import httplib,urllib
# stałe
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# parametry muszą zostać zakodowane przed wysłaniem do serwera
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# parametry umieszcza się na końcu URL
URL+="?"+params
# połączenie
connexion=httplib.HTTPConnection(HOST)
# śledzenie
connexion.set_debuglevel(1)
# wysłanie żądania
connexion.request("GET",URL)
# przetwarzanie odpowiedzi
reponse=connexion.getresponse()
# treść
contenu=reponse.read()
print contenu,"\n"
# zamknięcie połączenia
connexion.close()
Uwagi:
- wiersze 8–10: wartości trzech parametrów wysłanych do serwisu internetowego;
- wiersz 13: należy je zakodować. Odbywa się to za pomocą metody urlencode modułu urllib. Moduł ten jest importowany w wierszu 3. Metoda przyjmuje jako parametr słownik {param1:val1, param2:val2, ...};
- wiersz 15: w poleceniu GET (wiersz 21) klient musi umieścić zakodowane parametry na końcu URL serwisu internetowego;
- kolejne wiersze zostały już omówione.
12.2.3. Wyniki
Uwagi:
- wiersz 2: zwróć uwagę na sposób zakodowania parametrów (nazwisko, imię, wiek);
- wiersz 8: odpowiedź serwisu internetowego.
12.2.4. Klient POST
Klient POST jest analogiczny do klienta GET, z tą różnicą, że zakodowane parametry nie są już częścią docelowego URL. Zostały one przekazane jako trzeci argument żądania POST (wiersz 19).
# -*- coding=utf-8 -*-
import httplib,urllib
# stałe
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# parametry muszą zostać zakodowane przed wysłaniem na serwer
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# połączenie
connexion=httplib.HTTPConnection(HOST)
# śledzenie
connexion.set_debuglevel(1)
# wysyłanie żądania
connexion.request("POST",URL,params)
# przetwarzanie odpowiedzi
reponse=connexion.getresponse()
# treść
contenu=reponse.read()
print contenu,"\n"
# zamknięcie połączenia
connexion.close()
12.2.5. Wyniki
Uwagi:
- zwróć uwagę na wiersz 2, gdzie pokazano metodę używaną przez klienta POST do wysyłania zakodowanych parametrów:
- nagłówek HTTP Content-Length wskazuje liczbę znaków, które zostaną wysłane do serwisu internetowego;
- po tym nagłówku HTTP następuje pusty wiersz sygnalizujący koniec nagłówków HTTP;
- następnie wysyłanych jest 39 znaków zakodowanych parametrów.
- wiersz 8: odpowiedź serwisu internetowego.
12.3. Pobieranie zmiennych środowiskowych z serwisu internetowego
12.3.1. Serwis internetowy
Skrypt CGI w języku Python działa w środowisku systemowym, które posiada atrybuty. Jego atrybuty i ich wartości są dostępne w słowniku o nazwie os.environ.
#!D:\Programs\ActivePython\Python2.7.2\python.exe
import os
# nagłówki
print "Content-Type: text/plain\n"
# informacje o środowisku
for (cle,valeur) in os.environ.items():
print "%s : %s" % (cle,valeur)
Uwagi:
- wiersz 3: należy zaimportować moduł os, aby uzyskać dostęp do zmiennych „systemowych”.
Jeśli uruchomimy bezpośrednio powyższy skrypt (c.a.d jako skrypt konsolowy, a nie cgi), w konsoli otrzymamy następujące wyniki:
W przeglądarce internetowej (w tym przypadku wykonywany jest skrypt CGI) otrzymujemy następujące wyniki:
![]() |
Należy zauważyć, że w zależności od kontekstu wykonania uzyskane środowisko nie jest takie samo.
12.3.2. Zaprogramowany klient
# -*- coding=utf-8 -*-
import httplib
# stałe
HOST="localhost"
URL="/cgi-bin/web_04.py"
# połączenie
connexion=httplib.HTTPConnection(HOST)
# wysyłanie żądania
connexion.request("GET", URL)
# przetwarzanie odpowiedzi
reponse=connexion.getresponse()
# treść
print reponse.read()
12.3.3. Wyniki
Należy zauważyć, że zaprogramowany klient nie otrzymuje dokładnie takiej samej odpowiedzi jak przeglądarka internetowa. Wynika to z faktu, że przeglądarka wysłała do serwera internetowego informacje, które zostały wykorzystane przez serwer do utworzenia odpowiedzi. W tym przypadku zaprogramowany klient nie wysłał żadnych informacji o sobie.




