Programación CGI en Python
Qué es CGI
CGI es mantenido actualmente por NCSA, que define CGI de la siguiente manera:
CGI (Common Gateway Interface), Interfaz de Pasarela Común, es un programa que se ejecuta en un servidor, como un servidor HTTP, y proporciona una interfaz con páginas HTML del cliente.
Nota:
A partir de Python 3.11, el módulo cgi está oficialmente marcado como "obsoleto" (Deprecated) y se elimina a partir de Python 3.13.
Se recomienda migrar aFlask、DjangooFastAPIy otros marcos web modernos.
Navegación web
Para comprender mejor cómo funciona CGI, podemos partir del proceso de hacer clic en un enlace o URL en una página web:
- 1. Usa tu navegador para acceder a la URL y conectarte al servidor web HTTP.
- 2. El servidor web recibe la información de la solicitud, analiza la URL y verifica si el archivo solicitado existe en el servidor. Si existe, devuelve el contenido del archivo; de lo contrario, devuelve un mensaje de error.
- 3. El navegador recibe la información del servidor y muestra el archivo recibido o el mensaje de error.
Los programas CGI pueden ser scripts de Python, scripts de PERL, scripts de SHELL, programas en C o C++, etc.
Diagrama de arquitectura CGI

Soporte y configuración del servidor web
Antes de realizar programación CGI, asegúrate de que tu servidor web sea compatible con CGI y de que ya esté configurado el manejador de CGI.
Configuración de soporte CGI en Apache:
Establece el directorio CGI:
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
Todos los programas CGI que ejecuta el servidor HTTP se guardan en un directorio preconfigurado. Este directorio se denomina directorio CGI y, por convención, se llama /var/www/cgi-bin.
La extensión de los archivos CGI es.cgi, Python también puede usar la extensión.pyla extensión.
Por defecto, el directorio cgi-bin configurado en el servidor Linux es /var/www.
Si deseas especificar otro directorio para ejecutar scripts CGI, puedes modificar el archivo de configuración httpd.conf, como se muestra a continuación:
<Directory "/var/www/cgi-bin"> AllowOverride None Options +ExecCGI Order allow,deny Allow from all </Directory>
Agrega la extensión .py en AddHandler para poder acceder a los archivos de scripts de Python que terminan en .py:
AddHandler cgi-script .cgi .pl .py
Primer programa CGI
Creamos el primer programa CGI con Python, con el nombre de archivo hello.py, ubicado en el directorio /var/www/cgi-bin, con el siguiente contenido:
#!/usr/bin/python # -*- coding: UTF-8 -*- print "Content-type:text/html" print # 空行,告诉服务器结束头部 print '<html>' print '<head>' print '<meta charset="utf-8">' print '<title>Hello World - 我的第一个 CGI 程序!</title>' print '</head>' print '<body>' print '<h2>Hello World! 我是来自Ejemplo的第一CGI程序</h2>' print '</body>' print '</html>'
Después de guardar el archivo, modifica hello.py y cambia sus permisos a 755:
chmod 755 hello.py
El programa anterior se accede en el navegadorhttp://localhost/cgi-bin/hello.pyEl resultado mostrado es el siguiente:
Hello World! 我是来自Ejemplo的第一CGI程序
El script hello.py es un script de Python simple. La primera línea de salida del script, "Content-type:text/html", se envía al navegador y le indica que el tipo de contenido mostrado es "text/html".
Usa print para generar una línea en blanco que indique al servidor el final de la información de cabecera.
Cabeceras HTTP
" Content-type:text/html" en el contenido del archivo hello.py es parte de la cabecera HTTP. Se envía al navegador para indicarle el tipo de contenido del archivo.
El formato de la cabecera HTTP es el siguiente:
HTTP 字段名: 字段内容
Por ejemplo:
Content-type: text/html
La siguiente tabla describe la información que se usa frecuentemente en las cabeceras HTTP en programas CGI:
| Cabeza | Descripción |
|---|---|
| Content-type: | Información MIME de la solicitud correspondiente a la entidad. Por ejemplo: Content-type:text/html |
| Expires: Date | Fecha y hora de caducidad de la respuesta |
| Location: URL | Se utiliza para redirigir al receptor a una ubicación distinta de la URL solicitada para completar la solicitud o identificar un nuevo recurso |
| Last-modified: Date | Hora de última modificación del recurso solicitado |
| Content-length: N | Longitud del contenido de la solicitud |
| Set-Cookie: String | Configurar cookies HTTP |
Variables de entorno CGI
Todos los programas CGI reciben las siguientes variables de entorno, que desempeñan un papel importante en los programas CGI:
| Nombre de la variable | Descripción |
|---|---|
| CONTENT_TYPE | El valor de esta variable de entorno indica el tipo MIME de la información transmitida. Actualmente, la variable de entorno CONTENT_TYPE suele ser: application/x-www-form-urlencoded, lo que significa que los datos provienen de un formulario HTML. |
| CONTENT_LENGTH | Si la transferencia de información entre el servidor y el programa CGI es mediante POST, esta variable de entorno es el número de bytes de datos válidos que se pueden leer desde la entrada estándar STDIN. Esta variable de entorno debe usarse al leer los datos de entrada. |
| HTTP_COOKIE | Contenido de las cookies en el cliente. |
| HTTP_USER_AGENT | Proporciona información del navegador del cliente que incluye el número de versión u otros datos propietarios. |
| PATH_INFO | El valor de esta variable de entorno representa otra información de ruta que sigue inmediatamente al nombre del programa CGI. A menudo aparece como parámetro del programa CGI. |
| QUERY_STRING | Si la transferencia de información entre el servidor y el programa CGI es mediante GET, el valor de esta variable de entorno es la información transmitida. Esta información va después del nombre del programa CGI, separada por un signo de interrogación '?'. |
| REMOTE_ADDR | El valor de esta variable de entorno es la dirección IP del cliente que envía la solicitud, por ejemplo, 192.168.1.67 de arriba. Este valor siempre está presente. Además, es el único identificador que el cliente web debe proporcionar al servidor web, y se puede usar en programas CGI para distinguir diferentes clientes web. |
| REMOTE_HOST | El valor de esta variable de entorno contiene el nombre de host del cliente que envía la solicitud CGI. Si no se admite la consulta deseada, no es necesario definir esta variable de entorno. |
| REQUEST_METHOD | Proporciona el método con el que se invoca el script. Para scripts que usan el protocolo HTTP/1.0, solo GET y POST tienen significado. |
| SCRIPT_FILENAME | Ruta completa del script CGI |
| SCRIPT_NAME | Nombre del script CGI |
| SERVER_NAME | Este es el nombre de host, alias o dirección IP de tu servidor web. |
| SERVER_SOFTWARE | El valor de esta variable de entorno contiene el nombre y el número de versión del servidor HTTP que invoca el programa CGI. Por ejemplo, el valor anterior es Apache/2.2.14(Unix). |
El siguiente es un script CGI simple que muestra las variables de entorno de CGI:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# filename:test.py
import os
print "Content-type: text/html"
print
print "<meta charset=\"utf-8\">"
print "<b>环境变量</b><br>";
print "<ul>"
for key in os.environ.keys():
print "<li><span style='color:green'>%30s </span> : %s </li>" % (key,os.environ[key])
print "</ul>"
Guarda el código anterior como test.py, cambia los permisos del archivo a 755 y el resultado de ejecución es el siguiente:

Métodos GET y POST
El navegador cliente transmite información al servidor mediante dos métodos: el método GET y el método POST.
Transmisión de datos usando el método GET
El método GET envía la información del usuario codificada al servidor. Los datos se incluyen en la URL de la página solicitada, separados por un signo "?", como se muestra a continuación:
http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2Algunos comentarios adicionales sobre las solicitudes GET:
- Las solicitudes GET se pueden almacenar en caché
- Las solicitudes GET permanecen en el historial del navegador
- Las solicitudes GET se pueden guardar como marcadores
- Las solicitudes GET no deben usarse al manejar datos sensibles
- Las solicitudes GET tienen limitación de longitud
- Las solicitudes GET solo deben usarse para recuperar datos
Ejemplo simple de URL: método GET
La siguiente es una URL simple que usa el método GET para enviar dos parámetros al programa hello_get.py:
/cgi-bin/test.py?name=Ejemplo&url=http://www.example.com
El siguiente es el código del archivo hello_get.py:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# filename:test.py
# CGI处理模块
import cgi, cgitb
# 创建 FieldStorage 的实例化
form = cgi.FieldStorage()
# 获取数据
site_name = form.getvalue('name')
site_url = form.getvalue('url')
print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Ejemplo CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2>%s官网:%s</h2>" % (site_name, site_url)
print "</body>"
print "</html>"
Después de guardar el archivo, modifica hello_get.py y cambia los permisos del archivo a 755:
chmod 755 hello_get.py
El resultado de la solicitud en el navegador:

Ejemplo simple de formulario: método GET
Lo siguiente es un formulario HTML que usa el método GET para enviar dos datos al servidor. El script del servidor al que se envía es también el archivo hello_get.py. El código de hello_get.html es el siguiente:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ejemplo(example.com)</title> </head> <body> <form action="../cgi-bin/hello_get.py" method="get"> 站点名称: <input type="text" name="name"> <br /> 站点 URL: <input type="text" name="url" /> <input type="submit" value="提交" /> </form> </body> </html>
Por defecto, el directorio cgi-bin solo puede contener archivos de script. Guardamos hello_get.html en el directorio test y modificamos los permisos del archivo a 755:
chmod 755 hello_get.html
La demostración en Gif es la siguiente:

Envío de datos usando el método POST
Usar el método POST para transmitir datos al servidor es más seguro y confiable. Información sensible como las contraseñas de usuario debe transmitirse mediante POST.
El siguiente también es hello_get.py, que puede procesar los datos de formularios POST enviados por el navegador:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# CGI处理模块
import cgi, cgitb
# 创建 FieldStorage 的实例化
form = cgi.FieldStorage()
# 获取数据
site_name = form.getvalue('name')
site_url = form.getvalue('url')
print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Ejemplo CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2>%s官网:%s</h2>" % (site_name, site_url)
print "</body>"
print "</html>"
Lo siguiente es el formulario que, mediante el método POST (method="post"), envía datos al script del servidor hello_get.py:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ejemplo(example.com)</title> </head> <body> <form action="../cgi-bin/hello_get.py" method="post"> 站点名称: <input type="text" name="name"> <br /> 站点 URL: <input type="text" name="url" /> <input type="submit" value="提交" /> </form> </body> </html>
La demostración en Gif es la siguiente:

Transmisión de datos de casillas de verificación (checkbox) mediante un programa CGI
checkbox se utiliza para enviar datos de una o varias opciones. El código HTML es el siguiente:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ejemplo(example.com)</title> </head> <body> <form action="../cgi-bin/checkbox.py" method="POST" target="_blank"> <input type="checkbox" name="ejemplo" value="on" /> Ejemplo <input type="checkbox" name="google" value="on" /> Google <input type="submit" value="选择站点" /> </form> </body> </html>
El siguiente es el código del archivo checkbox.py:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('google'):
google_flag = "是"
else:
google_flag = "否"
if form.getvalue('ejemplo'):
ejemplo_flag = "是"
else:
ejemplo_flag = "否"
print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Ejemplo CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> Ejemplo是否选择了 : %s</h2>" % ejemplo_flag
print "<h2> Google 是否选择了 : %s</h2>" % google_flag
print "</body>"
print "</html>"
Modificar los permisos de checkbox.py:
chmod 755 checkbox.py
Gif de demostración del acceso mediante el navegador:

Transmisión de datos de botones de opción (radio) mediante un programa CGI
Radio solo envía un dato al servidor. El código HTML es el siguiente:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ejemplo(example.com)</title> </head> <body> <form action="../cgi-bin/radiobutton.py" method="post" target="_blank"> <input type="radio" name="site" value="ejemplo" /> Ejemplo <input type="radio" name="site" value="google" /> Google <input type="submit" value="提交" /> </form> </body> </html>
El código del script radiobutton.py es el siguiente:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('site'):
site = form.getvalue('site')
else:
site = "提交数据为空"
print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Ejemplo CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 选中的网站是 %s</h2>" % site
print "</body>"
print "</html>"
Modificar los permisos de radiobutton.py:
chmod 755 radiobutton.py
Gif de demostración del acceso mediante el navegador:

Transmisión de datos de área de texto (textarea) mediante un programa CGI
Textarea envía datos de varias líneas al servidor. El código HTML es el siguiente:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ejemplo(example.com)</title> </head> <body> <form action="../cgi-bin/textarea.py" method="post" target="_blank"> <textarea name="textcontent" cols="40" rows="4"> 在这里输入内容... </textarea> <input type="submit" value="提交" /> </form> </body> </html>
El código del script textarea.py es el siguiente:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('textcontent'):
text_content = form.getvalue('textcontent')
else:
text_content = "没有内容"
print "Content-type:text/html"
print
print "<html>"
print "<head>";
print "<meta charset=\"utf-8\">"
print "<title>Ejemplo CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 输入的内容是:%s</h2>" % text_content
print "</body>"
print "</html>"
Modificar los permisos de textarea.py:
chmod 755 textarea.py
Gif de demostración del acceso mediante el navegador:

Transmisión de datos de lista desplegable mediante un programa CGI.
El código del cuadro desplegable HTML es el siguiente:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ejemplo(example.com)</title> </head> <body> <form action="../cgi-bin/dropdown.py" method="post" target="_blank"> <select name="dropdown"> <option value="ejemplo" selected>Ejemplo</option> <option value="google">Google</option> </select> <input type="submit" value="提交"/> </form> </body> </html>
El código del script dropdown.py es el siguiente:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('dropdown'):
dropdown_value = form.getvalue('dropdown')
else:
dropdown_value = "没有内容"
print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>Ejemplo CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 选中的选项是:%s</h2>" % dropdown_value
print "</body>"
print "</html>"
Modificar los permisos de dropdown.py:
chmod 755 dropdown.py
Gif de demostración del acceso mediante el navegador:

Uso de cookies en CGI
Una gran desventaja del protocolo http es que no verifica la identidad del usuario, lo que causa grandes inconvenientes a los programadores. La aparición de la función de cookie compensa esta deficiencia.
La cookie consiste en que, mientras el cliente accede al script, se escriben datos de registro en el disco duro del cliente a través del navegador de este. Cuando el cliente vuelva a acceder al script, se recuperan los datos, logrando así la función de identificación. La cookie se usa comúnmente en la verificación de identidad.
Sintaxis de cookies
El envío de la cookie http se realiza a través de la cabecera http, que precede a la transferencia del archivo. La sintaxis de la cabecera Set-Cookie es la siguiente:
Set-cookie:name=name;expires=date;path=path;domain=domain;secure
- name=name:Se debe configurar el valor de la cookie (el nombre no puede usar ";y ",carácter), cuando haya múltiples valores de name, use ";" para separar, por ejemplo:name1=name1;name2=name2;name3=name3。
- expires=date:Período de validez de la cookie, formato: expires="Wdy,DD-Mon-YYYY HH:MM:SS"
- path=path: Establecer la ruta compatible con la cookie. Si path es una ruta, la cookie es válida para todos los archivos y subdirectorios de ese directorio, por ejemplo: path="/cgi-bin/". Si path es un archivo, la cookie solo es válida para ese archivo, por ejemplo: path="/cgi-bin/cookie.cgi".
- domain=domain:Dominio donde la cookie es válida, por ejemplo: domain="www.example.com"
- secure:Si se proporciona este indicador, significa que la cookie solo puede transmitirse a través de un servidor https con protocolo SSL.
- La recepción de la cookie se logra mediante la variable de entorno HTTP_COOKIE. El programa CGI puede obtener la información de la cookie consultando esta variable.
Configuración de cookies
La configuración de Cookie es muy simple; la cookie se envía por separado en la cabecera http. El siguiente ejemplo establece name y expires en la cookie:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
#
print 'Content-Type: text/html'
print 'Set-Cookie: name="Ejemplo";expires=Wed, 28 Aug 2016 18:30:00 GMT'
print
print """
<html>
<head>
<meta charset="utf-8">
<title>Ejemplo(example.com)</title>
</head>
<body>
<h1>Cookie set OK!</h1>
</body>
</html>
"""
Guarde el código anterior como cookie_set.py y modifique los permisos de cookie_set.py:
chmod 755 cookie_set.py
El ejemplo anterior utiliza la cabecera Set-Cookie para configurar la información de la Cookie. En las opciones se establecen otros atributos de la Cookie, como el tiempo de expiración Expires, el dominio Domain y la ruta Path. Esta información se establece antes de "Content-type:text/html".
Recuperación de información de cookies
La página de recuperación de información de Cookie es muy simple. La información de Cookie se almacena en la variable de entorno HTTP_COOKIE del CGI, con el siguiente formato de almacenamiento:
key1=value1;key2=value2;key3=value3....
El siguiente es un programa CGI simple para recuperar información de cookie:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 导入模块
import os
import Cookie
print "Content-type: text/html"
print
print """
<html>
<head>
<meta charset="utf-8">
<title>Ejemplo(example.com)</title>
</head>
<body>
<h1>读取cookie信息</h1>
"""
if 'HTTP_COOKIE' in os.environ:
cookie_string=os.environ.get('HTTP_COOKIE')
c=Cookie.SimpleCookie()
c.load(cookie_string)
try:
data=c['name'].value
print "cookie data: "+data+"<br>"
except KeyError:
print "cookie 没有设置或者已过期<br>"
print """
</body>
</html>
"""
Guarde el código anterior como cookie_get.py y modifique los permisos de cookie_get.py:
chmod 755 cookie_get.py
El Gif de configuración de color de cookie anterior se muestra a continuación:

Ejemplo de carga de archivos
El formulario HTML para subir archivos necesita configurarenctypeel atributomultipart/form-data, el código es el siguiente:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ejemplo(example.com)</title>
</head>
<body>
<form enctype="multipart/form-data"
action="../cgi-bin/save_file.py" method="post">
<p>选中文件: <input type="file" name="filename" /></p>
<p><input type="submit" value="上传" /></p>
</form>
</body>
</html>
El código del archivo de script save_file.py es el siguiente:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import cgi, os
import cgitb; cgitb.enable()
form = cgi.FieldStorage()
# 获取文件名
fileitem = form['filename']
# 检测文件是否上传
if fileitem.filename:
# 设置文件路径
fn = os.path.basename(fileitem.filename)
open('/tmp/' + fn, 'wb').write(fileitem.file.read())
message = '文件 "' + fn + '" 上传成功'
else:
message = '文件没有上传'
print """\
Content-Type: text/html\n
<html>
<head>
<meta charset="utf-8">
<title>Ejemplo(example.com)</title>
</head>
<body>
<p>%s</p>
</body>
</html>
""" % (message,)
Guarde el código anterior como save_file.py y modifique los permisos de save_file.py:
chmod 755 save_file.py
El Gif de configuración de color de cookie anterior se muestra a continuación:

Si su sistema es Unix/Linux, debe reemplazar el separador de archivos. En Windows, solo necesita usar la instrucción open():
fn = os.path.basename(fileitem.filename.replace("\\", "/" ))
Diálogo de descarga de archivos
Primero creamos el archivo foo.txt en el directorio actual, para que el programa lo descargue.
La descarga de archivos se implementa configurando la cabecera HTTP. El código de la funcionalidad es el siguiente:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# HTTP 头部
print "Content-Disposition: attachment; filename=\"foo.txt\"";
print
# 打开文件
fo = open("foo.txt", "rb")
str = fo.read();
print str
# 关闭文件
fo.close()
Otras extensiones