Cómo usar Python en la web

De WikiCabal
Saltar a: navegación, buscar

Ícono de borrador

Éste artículo es un obra en proceso, por ello está cambiando diario.


Python puede usarse para implementar servidores, clientes y aplicaciones de web.

La librería estándar

La líbrería estándar de Python include los módulos http.server, http.client y cgi. En las siguientes secciones se muestran ejemplos mínimos del uso de estos módulos. Todos los ejemplos se encuentran en el repositorio python-in-the-web en github.

Servidor

Un servidor HTTP mínimo puede hacerse con el módulo http.server así:

from http.server import HTTPServer, SimpleHTTPRequestHandler

PORT = 1234
server_address = ('localhost', PORT)

Handler = SimpleHTTPRequestHandler

httpd = HTTPServer(server_address, Handler)

print("servidor en puerto", PORT)
httpd.serve_forever()

Se corre así:

$ python3 servidor.py
servidor en puerto 1234

El servidor genera un listado de los archivos en la carpeta actual. Si existe un archivo index.html en la carpeta actual, se va a mostrar su contenido en lugar del listado de archivos. Podemos comprobarlo ejecutando:

$ echo Página principal > index.html

Ahora al visitar o cargar de nuevo localhost:1234 veremos Página principal

El programa despliega un renglón en la consola por cada petición HTTP que atiende. En el ejemplo abajo el primer renglón es cuando se escribió localhost:1234 en el navegador. Los dos siguientes renglones son porque se escribió localhost:1234/noexiste en el navegador:

127.0.0.1 - - [29/Sep/2012 17:35:34] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [29/Sep/2012 17:36:06] code 404, message File not found
127.0.0.1 - - [29/Sep/2012 17:36:06] "GET /noexiste HTTP/1.1" 404 -

Common Gateway Interface

Es posible implementar tanto un servidor que ejecuta CGI como scripts CGI. Para un servidor CGI solo tenemos que remplazar el Handler SimpleHTTPRequestHandler por CGIHTTPRequestHandler en el ejemplo anterior:

from http.server import HTTPServer, CGIHTTPRequestHandler

PORT = 1234
server_address = ('localhost', PORT)

Handler = CGIHTTPRequestHandler

httpd = HTTPServer(server_address, Handler)

print("servidor con CGI en puerto", PORT)
httpd.serve_forever()

Se corre así:

$ python3 server_cgi.py
  servidor con CGI en puerto 1234

Este servidor mostrará los documentos en la carpeta actual como el servidor anterior, pero además ejecutará un archivo *.py o *.pyw que sea ejecutable y esté en alguna de las carpetas cgi-bin/ o htbin/ dentro del directorio actual. Estas carpetas predeterminadas se pueden cambiar modificando la lista http.server.cgi_directories.

Un script CGI mínimo sería:

#!/usr/bin/env python

import cgitb
cgitb.enable()

print("Content-Type: text/plain;charset=utf-8")
print()

print("¡Hola Mundo!")

Los comandos import cgitb y cgitb.enable() son para que el script emita mensajes de depuración en caso de que algo falle, en lugar de que no envíe ninguna salida.

Si al script CGI lo llamamos hola.py en la carpeta cgi-bin/, y lo hacemos ejecutable con chmod u+x cgi-bin/hola.py, se puede navegar a la ruta localhost:1234/cgi-bin/hola.py para obtener ¡Hola Mundo! como resultado.

Estas son algunas de las líneas que envía a la consola el servidor con CGI para diferentes casos:

127.0.0.1 - - [01/Oct/2012 20:44:24] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [01/Oct/2012 20:45:10] "GET /cgi-bin/hola.py HTTP/1.1" 200 -
127.0.0.1 - - [01/Oct/2012 20:44:47] code 403, message CGI script is not a plain file ('/cgi-bin/')
127.0.0.1 - - [01/Oct/2012 20:44:47] "GET /cgi-bin/ HTTP/1.1" 403 -
127.0.0.1 - - [01/Oct/2012 20:44:59] code 404, message No such CGI script ('/cgi-bin/noexiste')
127.0.0.1 - - [01/Oct/2012 20:44:59] "GET /cgi-bin/noexiste HTTP/1.1" 404 -
127.0.0.1 - - [01/Oct/2012 20:56:20] code 403, message CGI script is not executable ('/cgi-bin/noejecutable')
127.0.0.1 - - [01/Oct/2012 20:57:18] "GET /cgi-bin/nopy HTTP/1.1" 200 -
Traceback (most recent call last):
  File "/home/pp/lib/python3.3/http/server.py", line 1128, in run_cgi
    os.execve(scriptfile, args, env)
OSError: [Errno 8] Exec format error: '/home/pp/Desktop/cgi-bin/nopy'
127.0.0.1 - - [01/Oct/2012 20:57:18] CGI script exit status 0x7f00

Cliente

El módulo urllib permite manejar peticiones HTTP. Este ejemplo lee el contenido de la URL proporcionada como parámetro en la línea de comando:

import sys
import urllib.request

f = urllib.request.urlopen( sys.argv[1] )
data = f.read()
print( data.decode('utf-8') )

Los datos que se reciben son bytes, por eso es necesario decodificar para obtener una cadena. Un ejemplo del uso:

$ python3 get_url.py http://python.org/

Hay más ejemplos del uso de urllib en uno de los HowTos mencionados al final.

Aplicación

Una aplicación puede estar formada por varios scripts CGI en Python. Se pueden localizar en diferentes carpetas, como blog/, wiki/, contacto/, etc. En caso de usar algo como server_cgi.py basado en el módulo http.server, se podrían agregar esas carpetas a http.server.cgi_directories.

En el siguiente ejemplo extendemos la funcionalidad del servidor básico CGI para que ejecute automáticamente index.py si se encuentra en alguna de las carpetas designadas para CGI, y agregamos un par de rutas para que sean CGI.

import BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer
import os

def send_head(self):
    """Version of send_head that finds index.py in CGI directories."""
    path = self.path
    # separate the query
    i = path.find( '?' )
    if i >= 0:
        query = path[ i: ]
        path = path[ : i ]
    else:
        query = ''
    if os.path.isdir( '.' + path ):  # os.getcwd() ?
        # check if index.py exists
        pathindexpy = os.path.join( path, 'index.py' )
        if os.path.exists( '.' + pathindexpy ):
            path = pathindexpy + query
            self.path = path
    if self.is_cgi():
        return self.run_cgi()
    else:
        return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)

PORT = 1234
server_address = ('localhost', PORT)

Handler = CGIHTTPServer.CGIHTTPRequestHandler

# overwrite the existing send_head() method
Handler.send_head = send_head

# add paths to the cgi directories
Handler.cgi_directories.append( '/contacto' )
Handler.cgi_directories.append( '/wiki' )
Handler.cgi_directories.append( '/blog' )

httpd = BaseHTTPServer.HTTPServer( ( server_address, Handler )

print("servidor con CGI en puerto", PORT)
httpd.serve_forever()

Otra opción es que la aplicación sea un solo script CGI e implementar un mecanismo para definir rutas y asociar una función o controlador a cada ruta. Ese mecanismo deberá separar la primera parte de la URL para identificarla como una ruta y entonces invocar la función o controlador que atenderá esa petición. Igualmente se puede implementar un mecanismo de plantillas para estandarizar o facilitar el diseño de las páginas que se generarán.

Frameworks

Una opción muy popular es utilizar un framework que ofrezca las características que se requieran. Existen full-stack frameworks y basic frameworks.


Enlaces externos