Python CGI 编程详解
                           
天天向上
发布: 2025-03-15 19:46:09

原创
742 人浏览过

CGI(Common Gateway Interface,通用网关接口)是 Web 服务器与外部应用程序通信的标准方式。Python 可以使用 CGI 编写动态 Web 页面,处理用户提交的表单数据等。

1. CGI 概述

CGI(Common Gateway Interface,通用网关接口) 是 Web 服务器与外部程序通信的标准方式。它允许 Web 服务器执行外部程序(如 Python 脚本),并将其输出作为 HTTP 响应返回给浏览器。

1.1 CGI 的作用

  • 处理 表单提交
  • 生成 动态网页
  • 访问数据库并返回 查询结果
  • 读取服务器端数据并 返回 JSON/XML

2. CGI 服务器配置

CGI 脚本通常放在 cgi-bin 目录下,且需要 执行权限

2.1 配置 Apache 服务器

  1. 启用 CGI 模块
   sudo a2enmod cgi
  1. 修改 Apache 配置
    编辑 /etc/apache2/sites-enabled/000-default.conf
   <Directory "/usr/lib/cgi-bin">
       Options +ExecCGI
       AddHandler cgi-script .cgi .py
   </Directory>
  1. 重启 Apache
   sudo systemctl restart apache2

2.2 使用 Python 内置 HTTP 服务器

python3 -m http.server --cgi 8000

访问 http://localhost:8000/cgi-bin/test.py 即可执行 CGI 脚本。


3. 编写 Python CGI 脚本

创建 cgi-bin/hello.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("Content-Type: text/html")  # HTTP 头部
print()  # 空行,分隔头部和内容
print("<html><body><h2>Hello, CGI!</h2></body></html>")

赋予执行权限:

chmod +x cgi-bin/hello.py

浏览器访问:

http://localhost/cgi-bin/hello.py

4. 处理 GET 和 POST 请求

4.1 处理 GET 请求

如果访问:

http://localhost/cgi-bin/get.py?name=Tom&age=25

可以读取 URL 参数:

#!/usr/bin/python3
import cgi

print("Content-Type: text/html\n")

form = cgi.FieldStorage()
name = form.getvalue("name", "匿名")
age = form.getvalue("age", "未知")

print(f"<html><body><h2>姓名: {name}, 年龄: {age}</h2></body></html>")

4.2 处理 POST 请求

HTML 表单:

<form action="/cgi-bin/post.py" method="post">
    名字: <input type="text" name="username">
    <input type="submit" value="提交">
</form>

Python 处理:

#!/usr/bin/python3
import cgi

form = cgi.FieldStorage()
username = form.getvalue("username", "游客")

print("Content-Type: text/html\n")
print(f"<h2>你好, {username}!</h2>")

5. 读取 CGI 环境变量

服务器通过环境变量传递信息:

#!/usr/bin/python3
import os

print("Content-Type: text/html\n")
print("<html><body><h2>环境变量</h2><ul>")
for key, value in os.environ.items():
    print(f"<li><b>{key}</b>: {value}</li>")
print("</ul></body></html>")

6. 处理文件上传

HTML 上传表单:

<form action="/cgi-bin/upload.py" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="上传">
</form>

Python 处理:

#!/usr/bin/python3
import cgi, os

form = cgi.FieldStorage()
file_item = form["file"]

if file_item.filename:
    filename = os.path.basename(file_item.filename)
    with open(f"/tmp/{filename}", "wb") as f:
        f.write(file_item.file.read())

    print("Content-Type: text/html\n")
    print(f"<h2>文件 {filename} 上传成功!</h2>")

7. 使用 Cookie

7.1 设置 Cookie

import http.cookies

cookie = http.cookies.SimpleCookie()
cookie["username"] = "Tom"
cookie["username"]["max-age"] = 3600

print("Content-Type: text/html")
print(cookie)
print()
print("<h2>Cookie 已设置!</h2>")

7.2 读取 Cookie

import os, http.cookies

cookie_string = os.environ.get("HTTP_COOKIE", "")
cookies = http.cookies.SimpleCookie(cookie_string)
username = cookies.get("username")

print("Content-Type: text/html\n")
print(f"<h2>欢迎回来, {username.value if username else '游客'}!</h2>")

8. Session 管理

CGI 不能直接管理 Session,需要手动实现:

import uuid, os

session_id = str(uuid.uuid4())
session_dir = "/tmp/sessions"
os.makedirs(session_dir, exist_ok=True)

with open(f"{session_dir}/{session_id}", "w") as f:
    f.write("user=Tom")

print("Content-Type: text/html")
print(f"Set-Cookie: session_id={session_id}")
print()
print("<h2>Session 创建成功!</h2>")

9. 安全性

  • 避免 XSS:使用 html.escape() 处理用户输入:
  import html
  safe_input = html.escape("<script>alert('XSS')</script>")
  • 避免 SQL 注入:使用参数化查询:
  cursor.execute("SELECT * FROM users WHERE name = %s", (username,))
  • 防止文件上传攻击
  allowed_types = ["image/png", "image/jpeg"]
  if file_item.type not in allowed_types:
      print("不允许的文件类型")

10. CGI vs. WSGI

特性CGIWSGI
进程管理每个请求新进程运行一个长期进程
性能
适用场景小型项目现代 Web 应用

WSGI 推荐使用 Flask 或 Django 代替 CGI。


11. 部署 CGI

  1. 服务器安装 Apache 并启用 cgi
  2. 确保 cgi-bin 目录可执行:
   chmod +x /usr/lib/cgi-bin/
  1. 上传 *.py CGI 脚本,访问 URL。

12. CGI 实战项目:构建一个留言板

为了更好地理解 CGI 编程,我们使用 Python + CGI + SQLite 实现一个简单的 Web 留言板,用户可以提交留言并在页面上显示所有留言。


12.1 项目结构

/var/www/cgi-bin/
│── index.html          # 前端 HTML 页面
│── post_message.py     # 处理留言的 CGI 脚本
│── get_messages.py     # 获取所有留言的 CGI 脚本
│── messages.db         # SQLite 数据库文件

12.2 创建 SQLite 数据库

首先,我们创建一个 SQLite 数据库 messages.db 并添加 messages 表:

#!/usr/bin/python3
import sqlite3

conn = sqlite3.connect("messages.db")
cursor = conn.cursor()

cursor.execute("""
CREATE TABLE IF NOT EXISTS messages (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    message TEXT NOT NULL
)
""")

conn.commit()
conn.close()
print("数据库初始化完成")

执行该脚本,创建数据库。


12.3 创建留言 HTML 页面

新建 index.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>留言板</title>
</head>
<body>
    <h2>留言板</h2>
    <form action="/cgi-bin/post_message.py" method="post">
        <label>姓名: <input type="text" name="name" required></label><br>
        <label>留言: <textarea name="message" required></textarea></label><br>
        <input type="submit" value="提交">
    </form>

    <h3>所有留言</h3>
    <div id="messages"></div>

    <script>
        async function fetchMessages() {
            const response = await fetch('/cgi-bin/get_messages.py');
            document.getElementById('messages').innerHTML = await response.text();
        }
        fetchMessages();
    </script>
</body>
</html>

12.4 处理用户留言

创建 post_message.py 处理留言提交:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import cgi, sqlite3

print("Content-Type: text/html\n")

# 获取表单数据
form = cgi.FieldStorage()
name = form.getvalue("name", "匿名")
message = form.getvalue("message", "")

if not message:
    print("<h3>留言内容不能为空!</h3>")
    exit()

# 连接数据库
conn = sqlite3.connect("messages.db")
cursor = conn.cursor()

# 插入数据
cursor.execute("INSERT INTO messages (name, message) VALUES (?, ?)", (name, message))
conn.commit()
conn.close()

# 页面跳转
print("<h3>留言提交成功!<a href='/index.html'>返回</a></h3>")

12.5 获取所有留言

创建 get_messages.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sqlite3

print("Content-Type: text/html\n")

# 连接数据库
conn = sqlite3.connect("messages.db")
cursor = conn.cursor()

# 读取所有留言
cursor.execute("SELECT name, message FROM messages ORDER BY id DESC")
messages = cursor.fetchall()
conn.close()

# 渲染 HTML
if messages:
    for name, message in messages:
        print(f"<p><b>{name}:</b> {message}</p>")
else:
    print("<p>暂无留言</p>")

13. CGI 进阶优化

14.1 优化 CGI 处理性能

CGI 在每次请求时都会创建新进程,影响性能,可使用:

  • FastCGI(nginx + uwsgi)
  • WSGI(Flask、Django)
  • 持久化连接(如 mod_wsgi

14.2 处理并发请求

CGI 适用于小型应用,但不适合高并发。可使用:

  • Nginx + uWSGI
  • Python Flask/Django

示例 Flask 版本:

from flask import Flask, request, render_template
import sqlite3

app = Flask(__name__)

@app.route('/')
def index():
    return render_template("index.html")

@app.route('/post', methods=['POST'])
def post_message():
    name = request.form.get("name", "匿名")
    message = request.form.get("message", "")
    conn = sqlite3.connect("messages.db")
    cursor = conn.cursor()
    cursor.execute("INSERT INTO messages (name, message) VALUES (?, ?)", (name, message))
    conn.commit()
    conn.close()
    return "留言提交成功"

if __name__ == '__main__':
    app.run(debug=True)

这样就用 Flask 替代了 CGI,提高了性能。


15. CGI 安全防护

15.1 防止 SQL 注入

永远使用 参数化查询

cursor.execute("INSERT INTO messages (name, message) VALUES (?, ?)", (name, message))

避免:

cursor.execute(f"INSERT INTO messages (name, message) VALUES ('{name}', '{message}')")  # SQL 注入风险

15.2 防止 XSS(跨站脚本攻击)

使用 html.escape() 处理用户输入:

import html
safe_message = html.escape(message)

通过本教程,我们学会了:
CGI 服务器配置
Python CGI 处理 GET/POST
CGI 处理文件上传
CGI 使用 Cookie 和 Session
构建留言板应用
安全性防护(SQL 注入 & XSS)

更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *