Web Server con Python

Para crear un Web Server con Python es necesario estar familiarizados con algunos conceptos básicos de hilos de ejecución y protocolos de comunicación. En este ejemplo el Web Server devuelve un página HTML y una imagen JPG dentro de la misma página para ejemplificar como devolver texto y contenido binario en distintas peticiones que al final se muestran como una misma página en el navegador.

No es muy complicado crear un Servidor Web en Python, te mostramos el código y te explicamos cada paso:

import socketserver
from http import server

#decodigo.com

PAGINA_HTML="""\
<html>
<head>
<title>Ejemplo Web Server</title>
</head>
<body>
<center><h1>Ejemplo Web Server</h1></center>
<center><img src="imagen.jpg" width="400" height="225"></center>
</body>
</html>
"""

class RequestHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(301)
            self.send_header('Location', '/index.html')
            self.end_headers()
        elif self.path == '/index.html':
            content = PAGINA_HTML.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.send_header('Content-Length', len(content))
            self.end_headers()
            self.wfile.write(content)
        elif self.path == '/imagen.jpg':
            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'image/jpeg')
            self.end_headers()
           
            fileObj = open("D:/ruta_de_la_imagen/imagen.jpg","rb")
            image = fileObj.read()
            self.wfile.write(image)

        else:
            self.send_error(404)
            self.end_headers()

class ThreadedHTTPServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True

address = ('', 8000)
server = ThreadedHTTPServer(address, RequestHandler)
server.serve_forever()

Veamos con un poco más de detalle como funciona cada bloque de código. Lo primero que hacemos es declarar una cadena con el contenido HTML de la página index.html que devolveremos cuando sea invocado el servicio:

PAGINA_HTML="""\
<html>
<head>
<title>Ejemplo Web Server</title>
</head>
<body>
<center><h1>Ejemplo Web Server</h1></center>
<center><img src="imagen.jpg" width="400" height="225"></center>
</body>
</html>
"""

Posteriormente creamos una clase RequestHadler:

class RequestHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(301)
            self.send_header('Location', '/index.html')
            self.end_headers()
        elif self.path == '/index.html':
            content = PAGINA_HTML.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.send_header('Content-Length', len(content))
            self.end_headers()
            self.wfile.write(content)
        elif self.path == '/imagen.jpg':
            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'image/jpeg')
            self.end_headers()
           
            fileObj = open("D:/ruta_de_la_imagen/imagen.jpg","rb")
            image = fileObj.read()
            self.wfile.write(image)

        else:
            self.send_error(404)
            self.end_headers()

Esta clase se encargará de responder a las peticiones Web que haga el usuario invocando las siguientes direcciones web:

  • ‘/’
  • ‘/index.html’
  • ‘/imagen.jpg’

Creamos una clase que nos permitirá lanzar un hilo de ejecución independiente para cada petición Web y a la que llamamos ThreadedHttpServer:

class ThreadedHTTPServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True

En las líneas finales de nuestro código iniciamos la ejecución de nuestro servidor Web de forma indefinida en el puerto 8000:

address = ('', 8000)
server = ThreadedHTTPServer(address, RequestHandler)
server.serve_forever()

Es necesario que exista una imagen real en el equipo donde estás ejecutando este script, en este ejemplo hemos especificado la ruta y el acceso a la imagen en la línea de código que te mostramos a continuación:

fileObj = open("D:/ruta_de_la_imagen/imagen.jpg","rb")

Asegúrate de que sea una imagen válida y tengas permiso de lectura al archivo.

Algo importante que debes notar al crear un servicio como este, es que cuando de vuelves información a un usuario que hace la petición desde su navegador, es que no sólo debes devolver el contenido de un archivo HTML o los datos binarios de una imagen, también debes devolver información adicional que le permitirá al navegador entender que es lo que estás mandado. Esta información es la cabecera de una respuesta en una petición http. En nuestro ejemplo puedes ver que esto lo hacemos en las líneas siguientes:

            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'image/jpeg')
            self.end_headers()

Estamos devolviendo, entre otras cosas, el tipo de respuesta (200 exitosa), las condiciones para el recepción y tratamiento que el navegador debe dar a la información y el tipo de contenido que estamos devolviendo con «Content-Type» y la cadena «image/jpg». Finalmente, cerramos el bloque de la cabecera con la función end_headers().

Resultado

Al ejecutar el script en Windows por ejemplo, es probable que el sistema operativo te pida confirmación de seguridad de acceso. Esto es así porque estás utilizando un puerto que normalmente no se usa, el 8000. En Linux o Mac probablemente debas hacer algo similar.

Si escribes en el navegador: http://localhost:8000/

Obtendrás el siguiente resultado, pero con la imagen que hayas usado para el ejemplo:

Web Server con Python
Web Server con Python

Como puedes ver, crear un Web Server con Python no es muy difícil, pero si es necesario entender conceptos nuevos sobre comunicación por http para que todo funcione correctamente.

Más información en la página oficial de Python: https://docs.python.org/3/library/http.server.html

Más información en inglés: https://geekole.com/create-a-web-server-with-python/