12. Servicios web en Python
![]() |
Los scripts de Python pueden ejecutarse en un servidor WEB. Este último será el encargado de escuchar las solicitudes de los clientes. Desde el punto de vista del cliente, llamar a un servicio WEB equivale a solicitar el URL de dicho servicio. El cliente puede estar escrito en cualquier lenguaje, en particular en Python. Debemos saber «comunicarnos» con un servicio WEB, es decir, comprender el protocolo Http de comunicación entre un servidor web y sus clients. Ese es el objetivo de los programas que siguen.
Los scripts del servicio web serán ejecutados por el servidor web Apache de WampServer. Deben colocarse en un directorio específico: <WampServer>\bin\apache\apachex.y.z\cgi-bin, donde <WampServer> es la carpeta de instalación de WampServer y x.y.z es el directorio de version del servidor web Apache.
![]() |
No basta con colocar los scripts de Python en la carpeta <cgi-bin>. El script debe indicar en la primera línea la ruta del intérprete de Python que se va a utilizar. Esta ruta se incluye en forma de comentario:
El lector deberá adaptar esta ruta a su propio entorno.
12.1. Aplicación cliente/servidor de fecha y hora
Nuestro primer servicio web será un servicio de fecha y hora: el cliente recibe la fecha y la hora actuales.
12.1.1. El servidor
#!D:\Programas\ActivePython\Python2.7.2\python.exe
import time
# encabezados
print "Content-Type: text/plain\n"
# envío de la hora al cliente
# hora local: número de milisegundos desde el 01/01/1970
# «formato de visualización de fecha y hora
# d: día en 2 dígitos
# m: mes en dos dígitos
# y: año en dos dígitos
# H: hora 0,23
# M: minutos
# S: segundos
print time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
Notas:
- línea 6: el script debe generar por sí mismo algunos de los encabezados Http de la respuesta al cliente. Estos se sumarán a los encabezados Http generados por el propio servidor Apache. El encabezado Http de la línea 6 indica al cliente que se le va a enviar un recurso en formato text/plain, es decir, texto sin formato. Cabe destacar el «\n» al final del encabezado, que generará una línea en blanco tras el encabezado. Es obligatorio: es esta línea en blanco la que indica al cliente Http el final de los encabezados Http de la respuesta. A continuación viene el recurso solicitado por el cliente, en este caso un texto sin formato;
- línea 18: el recurso enviado al cliente es un texto que muestra la fecha y la hora actuales.
12.1.2. Dos pruebas
El script anterior se puede ejecutar directamente con el intérprete de Python en una ventana de comandos, tal y como hemos hecho hasta ahora. Esto permite eliminar posibles errores de sintaxis o de funcionamiento. Se obtiene el siguiente resultado:
Una vez probado el script de esta manera, podemos colocarlo en la carpeta <cgi-bin> del servidor Apache (véase el apartado 12). Iniciemos la aplicación WampServer. Esto inicia tanto un servidor web Apache como un SGBD MySQL. Por el momento, solo utilizaremos el servidor web. A continuación, con un navegador, solicitemos la siguiente URL: URL://localhost/cgi-bin/web_02.py :
![]() |
- en [1]: la URL solicitada;
- en [2]: la respuesta mostrada por el navegador;
- en [3]: el código fuente recibido por el navegador web. Es efectivamente el enviado por el script de Python.
Con ciertas herramientas (en este caso Firebug, un complemento del navegador Firefox) se puede acceder a los encabezados Http intercambiados con el servidor. Arriba, el navegador web ha recibido los siguientes encabezados Http:
En las líneas 7-8 se reconoce el encabezado Http enviado por el script de Python. Los anteriores han sido generados por el servidor web Apache.
12.1.3. Un cliente programado
Ahora escribimos un script que será cliente del servicio web anterior. Utilizamos las funcionalidades del módulo httplib, que facilita la escritura de clients Http.
# -*- coding=utf-8 -*-
import httplib,re
# constantes
HOST="localhost"
URL="/cgi-bin/web_02.py"
# iniciar sesión
connexion=httplib.HTTPConnection(HOST)
# seguimiento
connexion.set_debuglevel(1)
# envío de la solicitud
connexion.request("GET", URL)
# procesamiento de la respuesta
reponse=connexion.getresponse()
# contenido
contenu=reponse.read()
# cierre de la conexión
connexion.close()
print "------\n",contenu,"-----\n"
# recuperación de los elementos de la hora
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])
Notas:
- línea 3: el módulo re es necesario para las expresiones regulares, el módulo httplib para las funciones de clients Http;
- línea 9: se crea una conexión Http con el puerto 80 de HOST definido en la línea 6;
- línea 11: el seguimiento permite ver los encabezados de la solicitud del cliente y de la respuesta del servidor;
- línea 13: se solicita el URL del servicio web. Hay dos formas de solicitarlo: mediante un comando Http, GET o POST. La diferencia entre ambos se explica más adelante. Aquí se solicitará con el comando Http GET;
- línea 15: se lee la respuesta del servidor. Aquí se obtiene la respuesta completa: encabezados Http y recurso solicitado por el cliente. En su respuesta, el servidor pudo haber solicitado al cliente que se redirigiera. En este caso, el cliente httplib realiza automáticamente la redirección. Por lo tanto, la respuesta obtenida es la resultante de la redirección;
- línea 17: la respuesta se compone de los encabezados Http y del documento solicitado por el cliente. Para obtener solo los encabezados Http, se utilizará [reponse].getHeaders(). Para obtener el documento, se utiliza [reponse].read();
- línea 19: una vez obtenida la respuesta del servidor web, se cierra la conexión con este;
- sabemos que el documento enviado por el servidor es una línea de texto con el formato 15/06/11 14:56:36. Líneas 22-26: se utiliza una expresión regular para extraer los diferentes elementos de esta línea.
12.1.4. Resultados
Notas:
- línea 1: los encabezados Http enviados por el cliente al servidor web;
- líneas 2-6: los encabezados Http de la respuesta del servidor web;
- línea 8: el documento enviado por el servidor;
- línea 11: el resultado de su procesamiento;
12.2. Recuperación por parte del servidor de los parámetros enviados por el cliente
En el protocolo Http, un cliente dispone de dos métodos para pasar parámetros al servidor web:
- solicita el URL del servicio en forma de
GET url?param1=val1¶m2=val2¶m3=val3… HTTP/1.0
donde los valores válidos deben codificarse previamente para que ciertos caracteres reservados sean sustituidos por su valor hexadecimal.
- solicita el URL del servicio en la forma
y, a continuación, entre los encabezados Http enviados al servidor, coloca el siguiente encabezado:
El resto de los encabezados enviados por el cliente terminan con una línea en blanco. A continuación, puede enviar sus datos en el formato
donde los vali deben, al igual que en el método GET, codificarse previamente. El número de caracteres enviados al servidor debe ser N, donde N es el valor declarado en el encabezado:
12.2.1. El servicio web
El siguiente servicio web recibe 3 parámetros de su cliente: nombre, apellidos, edad. Los recupera de una especie de diccionario denominado cgi.FieldStorage proporcionado por el módulo cgi. El valor vali de un parámetro parami se obtiene mediante vali=cgi.FieldStorage().getlist("parami"). Se obtiene una matriz de:
- 0 elementos si el parámetro parami no está presente en la solicitud del cliente;
- 1 elemento si el parámetro parami está presente una vez en la solicitud del cliente;
- n elementos si el parámetro parami está presente n veces en la solicitud del cliente.
Una vez recuperados los parámetros, el script los devuelve al cliente.
#!D:\Programas\ActivePython\Python2.7.2\python.exe
import cgi
# encabezados
print "Content-Type: text/plain\n"
# recuperación por parte del servidor de la información enviada por el cliente
# aquí nombre=P&apellidos=N&edad=A
formulaire=cgi.FieldStorage()
# se devuelven al cliente
print "informations recues du service web [prenom=%s,nom=%s,age=%s]" % (formulaire.getlist("prenom"),formulaire.getlist("nom"),formulaire.getlist("age"))
Se puede realizar una prueba con un navegador web:
![]() |
En [1], el URL del servicio web. Cabe destacar la presencia de los tres parámetros: nombre, apellidos y edad. En [2], la respuesta del servicio web.
12.2.2. El cliente GET
# -*- coding=utf-8 -*-
import httplib,urllib
# constantes
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# los parámetros deben codificarse antes de enviarse al servidor
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# los parámetros se colocan al final del URL
URL+="?"+params
# conexión
connexion=httplib.HTTPConnection(HOST)
# seguimiento
connexion.set_debuglevel(1)
# envío de la solicitud
connexion.request("GET",URL)
# procesamiento de la respuesta
reponse=connexion.getresponse()
# contenido
contenu=reponse.read()
print contenu,"\n"
# cierre de la conexión
connexion.close()
Notas:
- líneas 8-10: los valores de los 3 parámetros enviados al servicio web;
- línea 13: hay que codificarlos. Esto se hace mediante el método urlencode del módulo urllib. Este módulo se importa en la línea 3. El método admite como parámetro un diccionario {param1:val1, param2:val2, ...};
- línea 15: en un comando GET (línea 21), el cliente debe colocar los parámetros codificados al final del URL del servicio web;
- las siguientes líneas ya se han visto.
12.2.3. Los resultados
Notas:
- línea 2: fíjese en la codificación de los parámetros (nombre, apellidos, edad);
- línea 8: la respuesta del servicio web.
12.2.4. El cliente POST
El cliente POST es análogo al cliente GET, salvo que los parámetros codificados ya no forman parte del URL de destino. Se pasan como tercer argumento de la solicitud POST (línea 19).
# -*- coding=utf-8 -*-
import httplib,urllib
# constantes
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# los parámetros deben codificarse antes de enviarse al servidor
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# conexión
connexion=httplib.HTTPConnection(HOST)
# seguimiento
connexion.set_debuglevel(1)
# envío de la solicitud
connexion.request("POST",URL,params)
# procesamiento de la respuesta
reponse=connexion.getresponse()
# contenido
contenu=reponse.read()
print contenu,"\n"
# cierre de la conexión
connexion.close()
12.2.5. Los resultados
Notas:
- cabe destacar la línea 2, el método utilizado por el cliente POST para enviar los parámetros codificados:
- el encabezado Http Content-Length indica el número de caracteres que se enviarán al servicio web;
- a continuación, este encabezado Http va seguido de una línea en blanco que indica el final de los encabezados Http;
- a continuación, se envían los 39 caracteres de los parámetros codificados.
- línea 8: la respuesta del servicio web.
12.3. Recuperación de las variables de entorno de un servicio web
12.3.1. El servicio web
El script CGI de Python se ejecuta en un entorno del sistema que posee atributos. Sus atributos y sus valores están disponibles en un diccionario os.environ.
#!D:\Programas\ActivePython\Python2.7.2\python.exe
import os
# encabezados
print "Content-Type: text/plain\n"
# información del entorno
for (cle,valeur) in os.environ.items():
print "%s : %s" % (cle,valeur)
Notas:
- línea 3: hay que importar el módulo os para disponer de las variables «del sistema».
Si se ejecuta directamente el script anterior (c.a.d, como script de consola y no como cgi), se obtienen en la consola los siguientes resultados:
En un navegador web (en este caso se ejecuta el script CGI), se obtienen los siguientes resultados:
![]() |
Cabe señalar que, según el contexto de ejecución, el entorno obtenido no es el mismo.
12.3.2. El cliente programado
# -*- coding=utf-8 -*-
import httplib
# constantes
HOST="localhost"
URL="/cgi-bin/web_04.py"
# conexión
connexion=httplib.HTTPConnection(HOST)
# envío de la solicitud
connexion.request("GET", URL)
# procesamiento de la respuesta
reponse=connexion.getresponse()
# contenido
print reponse.read()
12.3.3. Resultados
Cabe señalar que el cliente programado no recibe exactamente la misma respuesta que el navegador web. Esto se debe a que este último ha enviado al servidor web información que ha sido utilizada por el servidor web para crear su respuesta. En este caso, el cliente programado no ha enviado ninguna información sobre sí mismo.




