12. Webdienste in Python
![]() |
Python-Skripte können von einem Server WEB ausgeführt werden. Dieser Server nimmt die Client-Anfragen entgegen. Aus Sicht des Clients entspricht der Aufruf eines Dienstes WEB der Abfrage des URL dieses Dienstes. Der Client kann in einer beliebigen Sprache geschrieben werden, insbesondere in Python. Wir müssen wissen, wie man mit einem WEB-Dienst „kommuniziert“, d. h. das Kommunikationsprotokoll Http zwischen einem Webserver und seinen Clients verstehen. Das ist das Ziel der folgenden Programme.
Die Webdienst-Skripte werden vom Apache-Webserver von WampServer ausgeführt. Sie müssen in einem bestimmten Verzeichnis abgelegt werden: <WampServer>\bin\apache\apachex.y.z\cgi-bin, wobei <WampServer> das Installationsverzeichnis von WampServer ist und x.y.z die Version des Apache-Webservers angibt.
![]() |
Es reicht nicht aus, die Python-Skripte im Ordner <cgi-bin> abzulegen. Das Skript muss in der ersten Zeile den Pfad zum zu verwendenden Python-Interpreter angeben. Dieser Pfad wird als Kommentar angegeben:
Der Leser muss diesen Pfad an seine eigene Umgebung anpassen.
12.1. Client-Server-Anwendung für Datum und Uhrzeit
Unser erster Webdienst wird ein Datums- und Zeitdienst sein: Der Client erhält das aktuelle Datum und die aktuelle Uhrzeit.
12.1.1. Der Server
#!D:\Programme\ActivePython\Python2.7.2\python.exe
import time
# Kopfzeilen
print "Content-Type: text/plain\n"
# Zeitangabe an den Client
# lokale Zeit: Anzahl der Millisekunden seit dem 01.01.1970
# „Anzeigeformat für Datum und Uhrzeit“
# d: zweistelliger Tag
# m: zweistelliger Monat
# y: zweistelliges Jahr
# H: Stunde 0,23
# M: Minuten
# S: Sekunden
print time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
Hinweise:
- Zeile 6: Das Skript muss einige der HTTP-Header der Antwort an den Client selbst generieren. Diese werden zu den vom Apache-Server selbst generierten HTTP-Headern hinzugefügt. Der HTTP-Header in Zeile 6 teilt dem Client mit, dass ihm eine Ressource im Format text/plain gesendet wird, d. h. unformatierter Text. Beachten Sie das „\n“ am Ende des Headers, das eine Leerzeile hinter dem Header erzeugt. Dies ist zwingend erforderlich: Diese Leerzeile signalisiert dem HTTP-Client das Ende der HTTP-Header der Antwort. Anschließend folgt die vom Client angeforderte Ressource, in diesem Fall unformatierter Text;
- Zeile 18: Die an den Client gesendete Ressource ist ein Text, der das aktuelle Datum und die aktuelle Uhrzeit anzeigt.
12.1.2. Zwei Tests
Das vorstehende Skript kann direkt vom Python-Interpreter in einem Befehlsfenster ausgeführt werden, wie wir es bisher getan haben. So lassen sich mögliche Syntax- oder Funktionsfehler ausschließen. Man erhält folgendes Ergebnis:
Nachdem das Skript auf diese Weise getestet wurde, kann es im Ordner <cgi-bin> des Apache-Servers abgelegt werden (siehe Abschnitt 12). Starten wir die Anwendung WampServer. Dadurch werden sowohl ein Apache-Webserver als auch eine Datenbank MySQL gestartet. Wir werden vorerst nur den Webserver nutzen. Rufen wir dann mit einem Browser die folgende Seite auf: http://localhost/cgi-bin/web_02.py:
![]() |
- in [1]: die angeforderte URL;
- in [2]: die vom Browser angezeigte Antwort;
- in [3]: der vom Webbrowser empfangene Quellcode. Es handelt sich tatsächlich um den vom Python-Skript gesendeten Code.
Mit bestimmten Tools (hier Firebug, ein Plugin für den Firefox-Browser) kann man auf die mit dem Server ausgetauschten HTTP-Header zugreifen. Oben hat der Webbrowser die folgenden HTTP-Header empfangen:
In den Zeilen 7–8 erkennt man den vom Python-Skript gesendeten HTTP-Header. Die vorhergehenden wurden vom Apache-Webserver generiert.
12.1.3. Ein programmierter Client
Wir schreiben nun ein Skript, das als Client für den zuvor beschriebenen Webdienst fungiert. Dabei nutzen wir die Funktionen des Moduls httplib, das das Schreiben von HTTP-Clients erleichtert.
# -*- coding=utf-8 -*-
import httplib,re
# Konstanten
HOST="localhost"
URL="/cgi-bin/web_02.py"
# Verbindung
connexion=httplib.HTTPConnection(HOST)
# Verfolgung
connexion.set_debuglevel(1)
# Senden der Anfrage
connexion.request("GET", URL)
# Verarbeitung der Antwort
reponse=connexion.getresponse()
# Inhalt
contenu=reponse.read()
# Verbindung beenden
connexion.close()
print "------\n",contenu,"-----\n"
# Abruf der Zeitangaben
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])
Hinweise:
- Zeile 3: Das Modul re wird für reguläre Ausdrücke benötigt, das Modul httplib für die Funktionen der HTTP-Clients;
- Zeile 9: Es wird eine HTTP-Verbindung mit Port 80 von HOST hergestellt, das in Zeile 6 definiert ist;
- Zeile 11: Die Protokollierung ermöglicht es, die HTTP-Header der Client-Anfrage und der Server-Antwort einzusehen;
- Zeile 13: Das Webservice-Modul URL wird aufgerufen. Es gibt zwei Möglichkeiten, diese abzufragen: mithilfe des HTTP-Befehls GET oder POST. Der Unterschied zwischen den beiden wird weiter unten erläutert. Hier wird sie mit dem HTTP-Befehl GET abgefragt;
- Zeile 15: Die Antwort des Servers wird gelesen. Hier wird die gesamte Antwort abgerufen: HTTP-Header und die vom Client angeforderte Ressource. In seiner Antwort hat der Server den Client möglicherweise aufgefordert, eine Weiterleitung durchzuführen. In diesem Fall führt der Client httplib die Weiterleitung automatisch durch. Die erhaltene Antwort ist somit das Ergebnis dieser Weiterleitung;
- Zeile 17: Die Antwort besteht aus den HTTP-Headern und dem vom Client angeforderten Dokument. Um nur die HTTP-Header zu erhalten, verwendet man [reponse].getHeaders(). Um das Dokument zu erhalten, verwendet man [reponse].read();
- Zeile 19: Sobald die Antwort des Webservers vorliegt, wird die Verbindung zu diesem geschlossen;
- Es ist bekannt, dass das vom Server gesendete Dokument aus einer Textzeile in der Form 15.06.11 14:56:36 besteht. In den Zeilen 22–26 wird ein regulärer Ausdruck verwendet, um die verschiedenen Elemente dieser Zeile zu extrahieren.
12.1.4. Ergebnisse
Anmerkungen:
- Zeile 1: die vom Client an den Webserver gesendeten HTTP-Header;
- Zeilen 2–6: die HTTP-Header der Antwort des Webservers;
- Zeile 8: das vom Server gesendete Dokument;
- Zeile 11: das Ergebnis der Auswertung;
12.2. Abruf der vom Client gesendeten Parameter durch den Server
Im HTTP-Protokoll stehen einem Client zwei Methoden zur Verfügung, um Parameter an den Webserver zu übergeben:
- Er fordert den Dienst „URL“ in der Form
GET url?param1=val1¶m2=val2¶m3=val3… HTTP/1.0
wobei die Werte vali zuvor einer Kodierung unterzogen werden müssen, damit bestimmte reservierte Zeichen durch ihren Hexadezimalwert ersetzt werden.
- Er fragt den URL des Dienstes in der Form
und fügt dann unter den an den Server gesendeten HTTP-Headern den folgenden Header ein:
Die vom Client gesendeten Header-Zeilen enden jeweils mit einer Leerzeile. Anschließend kann er seine Daten in folgender Form senden
wobei die Zeichenfolge „vali“ – wie bei der Methode „GET“ – zuvor kodiert werden muss. Die Anzahl der an den Server gesendeten Zeichen muss N betragen, wobei N der im Header angegebene Wert ist:
12.2.1. Der Webdienst
Der folgende Webdienst erhält drei Parameter von seinem Client: nom, prenom, age. Er ruft diese aus einer Art Wörterbuch namens cgi.FieldStorage ab, das vom Modul cgi bereitgestellt wird. Der Wert vali eines Parameters parami wird über vali=cgi.FieldStorage().getlist("parami") ermittelt. Man erhält ein Array mit:
- 0 Elementen, wenn der Parameter parami nicht in der Client-Anfrage enthalten ist;
- 1 Element, wenn der Parameter parami einmal in der Client-Anfrage vorhanden ist;
- n Elemente, wenn der Parameter parami n-mal in der Client-Anfrage vorhanden ist.
Sobald die Parameter abgerufen wurden, sendet das Skript sie an den Client zurück.
#!D:\Programme\ActivePython\Python2.7.2\python.exe
import cgi
# Header
print "Content-Type: text/plain\n"
# Abruf der vom Client gesendeten Informationen durch den Server
# hier Vorname=P&Nachname=N&Alter=A
formulaire=cgi.FieldStorage()
# sie werden an den Client zurückgesendet
print "informations recues du service web [prenom=%s,nom=%s,age=%s]" % (formulaire.getlist("prenom"),formulaire.getlist("nom"),formulaire.getlist("age"))
Ein Test kann mit einem Webbrowser durchgeführt werden:
![]() |
In [1] ist die Antwort des Webdienstes URL enthalten. Zu beachten ist das Vorhandensein der drei Parameter nom, prenom und age. In [2] die Antwort des Webdienstes.
12.2.2. Der Client GET
# -*- coding=utf-8 -*-
import httplib,urllib
# Konstanten
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# Die Parameter müssen vor dem Senden an den Server kodiert werden
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# Die Parameter werden an das Ende des URL angehängt
URL+="?"+params
# Verbindung
connexion=httplib.HTTPConnection(HOST)
# Nachverfolgung
connexion.set_debuglevel(1)
# Senden der Anfrage
connexion.request("GET",URL)
# Verarbeitung der Antwort
reponse=connexion.getresponse()
# Inhalt
contenu=reponse.read()
print contenu,"\n"
# Schließen der Verbindung
connexion.close()
Anmerkungen:
- Zeilen 8–10: Die Werte der drei an den Webdienst gesendeten Parameter;
- Zeile 13: Diese müssen kodiert werden. Dies geschieht mithilfe der Methode urlencode des Moduls urllib. Dieses Modul wird in Zeile 3 importiert. Die Methode akzeptiert als Parameter ein Dictionary {param1:val1, param2:val2, ...};
- Zeile 15: In einem Befehl GET (Zeile 21) muss der Client die kodierten Parameter am Ende des URL des Webdienstes angeben;
- Die folgenden Zeilen wurden bereits behandelt.
12.2.3. Die Ergebnisse
Anmerkungen:
- Zeile 2: Beachten Sie die Kodierung der Parameter (Name, Vorname, Alter);
- Zeile 8: die Antwort des Webdienstes.
12.2.4. Der Client POST
Der Client POST entspricht dem Client GET, mit dem Unterschied, dass die kodierten Parameter nicht mehr Teil des Ziel-URL sind. Sie werden als drittes Argument der Anfrage POST (Zeile 19) übergeben.
# -*- coding=utf-8 -*-
import httplib,urllib
# Konstanten
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# Die Parameter müssen vor dem Senden an den Server kodiert werden
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# Verbindung
connexion=httplib.HTTPConnection(HOST)
# Verfolgung
connexion.set_debuglevel(1)
# Senden der Anfrage
connexion.request("POST",URL,params)
# Verarbeitung der Antwort
reponse=connexion.getresponse()
# Inhalt
contenu=reponse.read()
print contenu,"\n"
# Schließen der Verbindung
connexion.close()
12.2.5. Die Ergebnisse
Anmerkungen:
- Beachten Sie in Zeile 2 die vom Client POST verwendete Methode zum Senden der kodierten Parameter:
- Der HTTP-Header Content-Length gibt die Anzahl der Zeichen an, die an den Webdienst gesendet werden;
- auf diesen HTTP-Header folgt eine Leerzeile, die das Ende der HTTP-Header kennzeichnet;
- Anschließend werden die 39 Zeichen der kodierten Parameter gesendet.
- Zeile 8: Die Antwort des Webdienstes.
12.3. Abrufen der Umgebungsvariablen eines Webdienstes
12.3.1. Der Webdienst
Das Python-CGI-Skript wird in einer Systemumgebung ausgeführt, die über Attribute verfügt. Diese Attribute und ihre Werte sind in einem Wörterbuch mit dem Namen „os.environ“ verfügbar.
#!D:\Programme\ActivePython\Python2.7.2\python.exe
import os
# Header
print "Content-Type: text/plain\n"
# Umgebungsinformationen
for (cle,valeur) in os.environ.items():
print "%s : %s" % (cle,valeur)
Hinweise:
- Zeile 3: Das Modul os muss importiert werden, um auf die „System“-Variablen zugreifen zu können.
Wenn man das obige Skript direkt ausführt (c.a.d als Konsolenskript und nicht als CGI), erhält man in der Konsole folgende Ergebnisse:
In einem Webbrowser (dort wird das CGI-Skript ausgeführt) erhält man folgende Ergebnisse:
![]() |
Es ist zu beachten, dass die resultierende Umgebung je nach Ausführungskontext unterschiedlich ist.
12.3.2. Der programmierte Client
# -*- coding=utf-8 -*-
import httplib
# Konstanten
HOST="localhost"
URL="/cgi-bin/web_04.py"
# Verbindung
connexion=httplib.HTTPConnection(HOST)
# Senden der Anfrage
connexion.request("GET", URL)
# Verarbeitung der Antwort
reponse=connexion.getresponse()
# Inhalt
print reponse.read()
12.3.3. Ergebnisse
Es ist zu beachten, dass der programmierte Client nicht genau dieselbe Antwort erhält wie der Webbrowser. Das liegt daran, dass der Webbrowser Informationen an den Webserver gesendet hat, die dieser zur Erstellung seiner Antwort verwendet hat. Der programmierte Client hat hier keine Informationen über sich selbst gesendet.




