Python3(31) CGI 编程
在 Web 开发领域,通用网关接口(CGI)为服务器与客户端之间的交互提供了有效的途径。Python 凭借其强大的功能和简洁的语法,在 CGI 编程中表现出色。这篇博客将结通过代码示例深入学习 Python3 的 CGI 编程知识,方便日后复习回顾。
一、CGI 基础概念
1.1 什么是 CGI
CGI(Common Gateway Interface)即通用网关接口,它是运行在服务器上的一段程序,主要作用是提供服务器与客户端 HTML 页面的交互接口。比如当你在网页上点击链接或提交表单时,CGI 程序就可能在服务器端被触发,处理相关请求并返回相应结果。常见的 CGI 程序可以用 Python、PERL、SHELL、C 或 C++ 等语言编写。
1.2 CGI 工作流程
- 客户端请求:用户使用浏览器访问 URL,浏览器与 HTTP 服务器建立连接并发送请求信息。
- 服务器处理:服务器接收到请求后,解析 URL 并查找对应的文件。如果找到,就将文件内容返回给浏览器;若找不到,则返回错误信息。若请求涉及 CGI 程序,服务器会执行该程序。
- 浏览器显示:浏览器接收服务器返回的信息,并根据信息类型(如 HTML、文本等)进行相应显示。
二、Web 服务器支持与配置(以 Apache 为例)
在进行 Python CGI 编程前,需确保 Web 服务器支持 CGI 并完成相关配置。
设置 CGI 目录:在 Apache 服务器中,通常将 CGI 程序放在特定目录,如/var/www/cgi-bin
。可以通过修改httpd.conf
配置文件来指定 CGI 目录:
ScriptAlias /cgi-bin/ /var/www/cgi-bin/
配置目录权限:确保 CGI 目录有正确的权限设置,允许服务器执行其中的脚本:
<Directory "/var/www/cgi-bin">AllowOverride NoneOptions +ExecCGIOrder allow,denyAllow from all
</Directory>
添加文件后缀支持:若想用.py
作为 Python 脚本的扩展名,需在配置文件中添加相应设置:
AddHandler cgi-script .cgi .pl .py
三、第一个 CGI 程序
下面用 Python 创建第一个 CGI 程序hello.py
,保存于/var/www/cgi-bin
目录:
#!/usr/bin/python3
print("Content-type:text/html")
print() # 空行,告诉服务器结束头部
print('<html>')
print('<head>')
print('<meta charset="utf-8">')
print('<title>Hello Word - 我的第一个CGI程序!</title>')
print('</head>')
print('<body>')
print('<h2>Hello Word!我是来自菜鸟教程的第一CGI程序</h2>')
print('</body>')
print('</html>')
后需修改文件权限为 755:
chmod 755 hello.py
该程序向浏览器发送了 HTML 内容,其中Content-type:text/html
是 HTTP 头部信息,用于告知浏览器返回内容的类型。空行用于分隔 HTTP 头部和内容主体。
四、HTTP 头部
HTTP 头部在 CGI 程序中用于向浏览器传递重要信息,常见的头部信息如下:
头 | 描述 | 示例 |
---|---|---|
Content-type | 请求的与实体对应的 MIME 信息 | Content-type:text/html |
Expires | 响应过期的日期和时间 | Expires: Wed, 28 Aug 2016 18:30:00 GMT |
Location | 重定向接收方到非请求 URL 的位置 | Location: http://www.example.com/newpage |
Last-modified | 请求资源的最后修改时间 | Last-modified: Tue, 15 Nov 2022 10:00:00 GMT |
Content-length | 请求的内容长度 | Content-length: 1024 |
Set-Cookie | 设置 Http Cookie | Set-Cookie: name="value"; expires=date; path=path; domain=domain; secure |
五、CGI 环境变量
CGI 程序会接收一系列环境变量,这些变量包含了客户端和服务器的相关信息,在编程中十分重要。例如:
- CONTENT_TYPE:指示传递来的信息的 MIME 类型,常见值为
application/x-www-form-urlencoded
,表示数据来自 HTML 表单。 - QUERY_STRING:若服务器与 CGI 程序通过 GET 方式传递信息,该变量保存传递的信息,信息跟在 CGI 程序名后,以
?
分隔。
下面的 CGI 脚本用于输出所有 CGI 环境变量:
#!/usr/bin/python3
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>")
保存为test.py
并修改权限后,在浏览器中访问可查看环境变量信息。
六、GET 和 POST 方法
浏览器通过 GET 和 POST 方法向服务器传递信息,二者在使用上有一定区别。
6.1 GET 方法
GET 方法将用户信息编码后附加在 URL 上传递给服务器,例如:http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
。GET 方法的特点如下:
- 可被缓存,会保留在浏览器历史记录中,还能被收藏为书签。
- 有长度限制,不适合处理敏感数据,主要用于取回数据。
下面是一个使用 GET 方法传递参数的 CGI 程序hello_get.py
:
#!/usr/bin/python3
import cgi, cgitb
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>菜鸟教程CGI测试实例</title>")
print("</head>")
print("<body>")
print("<h2>%s官网:%s</h2>" % (site_name, site_url))
print("</body>")
print("</html>")
可以通过 URL 直接传递参数,也可以通过 HTML 表单(设置method="get"
)传递参数。
6.2 POST 方法
POST 方法将数据放在请求体中传递,相比 GET 方法更安全可靠,适合传递敏感信息。hello_get.py
同样可以处理 POST 方法提交的数据,只需将 HTML 表单的method
属性改为post
即可。
七、传递不同类型数据
7.1 checkbox 数据
checkbox 用于提交多个选项数据,HTML 代码示例:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>菜鸟教程(runoob.com)</title>
</head>
<body><form action="/cgi-bin/checkbox.py" method="POST" target="_blank"><input type="checkbox" name="runoob" value="on" />菜鸟教程<input type="checkbox" name="google" value="on" />Google<input type="submit" value="选择站点" /></form>
</body>
</html>
对应的 Python 脚本checkbox.py
:
#!/usr/bin/python3
import cgi, cgitb
form = cgi.FieldStorage()
if form.getvalue('google'):google_flag = "是"
else:google_flag = "否"
if form.getvalue('runoob'):runoob_flag = "是"
else:runoob_flag = "否"
print("Content-type:text/html")
print()
print("<html>")
print("<head>")
print("<meta charset=\"utf-8\">")
print("<title>菜鸟教程CGI测试实例</title>")
print("</head>")
print("<body>")
print("<h2>菜鸟教程是否选择了 : %s</h2>" % runoob_flag)
print("<h2>Google是否选择了 : %s</h2>" % google_flag)
print("</body>")
print("</html>")
7.2 Radio 数据
Radio 只传递一个数据,HTML 代码示例:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>菜鸟教程(runoob.com)</title>
</head>
<body><form action="/cgi-bin/radiobutton.py" method="post" target="_blank"><input type="radio" name="site" value="runoob" />菜鸟教程<input type="radio" name="site" value="google" />Google<input type="submit" value="提交" /></form>
</body>
</html>
radiobutton.py
脚本代码:
#!/usr/bin/python3
import cgi, cgitb
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>菜鸟教程CGI测试实例</title>")
print("</head>")
print("<body>")
print("<h2>选中的网站是 %s</h2>" % site)
print("</body>")
print("</html>")
7.3 Textarea 数据
Textarea 用于传递多行数据,HTML 代码示例:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>菜鸟教程(runoob.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>
textarea.py
脚本代码:
#!/usr/bin/python3
import cgi, cgitb
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>菜鸟教程CGI测试实例</title>")
print("</head>")
print("<body>")
print("<h2>输入的内容是:%s</h2>" % text_content)
print("</body>")
print("</html>")
7.4 下拉数据
HTML 下拉框代码示例:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>菜鸟教程(runoob.com)</title>
</head>
<body><form action="/cgi-bin/dropdown.py" method="post" target="_blank"><select name="dropdown"><option value="runoob" selected>菜鸟教程</option><option value="google">Google</option></select><input type="submit" value="提交"/></form>
</body>
</html>
dropdown.py
脚本代码:
#!/usr/bin/python3
import cgi, cgitb
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>菜鸟教程CGI测试实例</title>")
print("</head>")
print("<body>")
print("<h2>选中的选项是:%s</h2>" % dropdown_value)
print("</body>")
print("</html>")
八、CGI 中使用 Cookie
8.1 Cookie 设置
Cookie 用于在客户端存储少量数据,弥补了 HTTP 协议无状态的不足。设置 Cookie 通过 HTTP 头部的Set-Cookie
实现,示例代码cookie_set.py
:
#!/usr/bin/python3
print('Set-Cookie: name="菜鸟教程";expires=Wed, 28 Aug 2016 18:30:00 GMT')
print('Content-Type: text/html')
print()
print("""
<html>
<head><meta charset="utf-8"><title>菜鸟教程(runoob.com)</title>
</head>
<body><h1>Cookie set OK!</h1>
</body>
</html>
""")
8.2 检索 Cookie 信息
检索 Cookie 信息需通过 CGI 环境变量HTTP_COOKIE
,示例代码cookie_get.py
:
#!/usr/bin/python3
import os
import http.cookies
print("Content-type: text/html")
print()
print("""
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<h1>读取cookie信息</h1>
""")
if 'HTTP_COOKIE' in os.environ:cookie_string = os.environ.get('HTTP_COOKIE')c = http.cookies.SimpleCookie()c.load(cookie_string)try:data = c['name'].valueprint("cookie data: " + data + "<br>")except KeyError:print("cookie没有设置或者已过去<br>")
print("""
</body>
</html>
""")
九、文件上传与下载
9.1 文件上传
HTML 表单需设置enctype="multipart/form-data"
来实现文件上传,示例代码:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>菜鸟教程(runoob.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>
对应的 Python 脚本save_file.py
:
#!/usr/bin/python3
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>菜鸟教程(runoob.com)</title>
</head>
<body><p>%s</p>
</body>
</html>
""" % (message,))
9.2 文件下载
文件下载通过设置 HTTP 头信息Content-Disposition
实现,示例代码:
#!/usr/bin/python3
# HTTP头部
print("Content-Disposition: attachment; filename=\"foo.txt\"")
print()
# 打开文件
fo = open("foo.txt", "rb")
str = fo.read()
print(str)
# 关闭文件
fo.close()
十、总结
Python3 的 CGI 编程为 Web 开发提供了一种强大的交互方式,通过掌握 CGI 的基础概念、Web 服务器配置、不同数据传递方式、Cookie 使用以及文件上传下载等知识,可以构建出功能丰富的 Web 应用程序。在实际开发中,应根据具体需求选择合适的方法,并注意安全和性能问题。希望这篇博客能帮助你更好地复习和巩固 Python3 CGI 编程知识,在 Web 开发领域灵活运用。