Python 3 – CGI编程

Python 3 – CGI编程

通用网关接口(Common Gateway Interface,简称CGI)是一组标准,用于定义Web服务器和自定义脚本之间如何交换信息。CGI规范目前由NCSA维护。

什么是CGI?

  • 通用网关接口(Common Gateway Interface,简称CGI)是用于外部网关程序与信息服务器(如HTTP服务器)进行接口操作的标准。

  • 当前版本是CGI/1.1,CGI/1.2正在制定中。

Web浏览

为了理解CGI的概念,让我们看看当我们单击超链接以浏览特定Web页面或URL时会发生什么。

  • 您的浏览器联系HTTP Web服务器,并要求URL,即文件名。

  • Web服务器解析URL并查找文件名。如果找到该文件,Web服务器则将其发送回浏览器;否则Web服务器会发送一个错误消息,说明您请求了错误的文件。

  • Web浏览器接收来自Web服务器的响应,并显示接收到的文件或错误消息。

然而,可以设置HTTP服务器,使其在请求特定目录中的文件时,不会将该文件发送回去;相反,它会将其作为程序执行,将该程序输出的任何内容发送回浏览器以显示。这个功能称为通用网关接口(CGI),而这些程序称为CGI脚本。这些CGI程序可以是Python脚本、PERL脚本、Shell脚本、C语言或C++语言程序等。

CGI架构图

Python 3 - CGI编程

Web服务器支持和配置

在进行CGI编程之前,请确保您的Web服务器支持CGI,并已配置为处理CGI程序。由HTTP服务器执行的所有CGI程序都保存在预先配置的目录中。该目录称为CGI目录,按照惯例,它的名称为/var/www/cgi-bin。默认情况下,Linux服务器配置为只运行在/var/www/cgi-bin目录中的脚本。如果您想指定其他任何目录来运行您的CGI脚本,请在httpd.conf文件中注释以下行−

<Directory "/var/www/cgi-bin">
   AllowOverride None
   Options ExecCGI
   Order allow,deny
   Allow from all
</Directory>

<Directory "/var/www/cgi-bin">
Options All
</Directory>

这里我们假设您的Web服务器已经成功运行,并且您可以运行任何其他的CGI程序(如Perl或Shell脚本等)。

第一个CGI程序

这是一个简单的链接,链接到一个名为 hello.py 的CGI脚本。该文件保存在/var/www/cgi-bin目录中,内容如下。在运行CGI程序之前,请确保您已使用UNIX命令 chmod 755 hello.py 改变文件的模式来使其可执行。

#!/usr/bin/python

print ("Content-type:text/html\r\n\r\n")
print ('<html>')
print ('<head>')
print ('<title>Hello Word - First CGI Program</title>')
print ('</head>')
print ('<body>')
print ('<h2>Hello Word! This is my first CGI program</h2>')
print ('</body>')
print ('</html>')

注意 − 脚本中的第一行必须是Python可执行程序的路径。在Linux中,它应该是#!/usr/bin/python3。

在您的浏览器中输入以下URL

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


## 你好世界!这是我的第一个CGI程序

这个hello.py脚本是一个简单的Python脚本,它在标准输出文件(即屏幕)上输出其输出。有一个重要的并且额外的特性可用于要打印的第一行 Content-type:text/html\r\n\r\n 。这一行被发送回浏览器,并指定要在浏览器屏幕上显示的内容类型。

现在您必须已经了解了CGI的基本概念,并且可以使用Python编写许多复杂的CGI程序。此脚本还可以与任何其他外部系统交互,以交换信息,例如RDBMS。

HTTP标头

Content-type:text/html\r\n\r\n 是HTTP头的一部分,用于发送到浏览器以了解内容。所有HTTP头将采用以下形式 –

HTTP字段名称:字段内容

例如:
内容类型:text/html\r\n\r\n

您在CGI编程中经常使用的其他几个重要的HTTP头。

序号 头文件和描述
1 Content-type: 定义返回文件的格式的MIME字符串。Example is Content-type:text/html
2 Expires: Date 信息失效的日期。它由浏览器用于决定何时需要刷新页面。有效的日期字符串格式为 01 Jan 1998 12:00:00 GMT。
3 Location: URL 返回代替请求的URL。您可以使用此字段将请求重定向到任何文件。
4 Last-modified: Date 资源的最后修改日期。
5 Content-length: N 返回的数据的长度(以字节为单位)。浏览器使用此值报告文件的估计下载时间。
6 Set-Cookie: String 设置通过 字符串 传递的cookie。

CGI环境变量

所有CGI程序都可以访问以下环境变量。在编写任何CGI程序时,这些变量都扮演着重要角色。

序号 变量名及描述
1 CONTENT_TYPE 内容的数据类型。当客户端向服务器发送附加内容(例如,文件上传)时使用。
2 CONTENT_LENGTH 查询信息的长度。仅在POST请求中可用。
3 HTTP_COOKIE 以键值对的形式返回已设置的cookie。
4 HTTP_USER_AGENT User-Agent请求头字段包含有关发起请求的用户代理的信息。这是指的 web 浏览器的名称。
5 PATH_INFO CGI脚本的路径。
6 QUERY_STRING 与GET方法请求一起发送的URL编码信息。
7 REMOTE_ADDR 发出请求的远程主机的IP地址。这对于日志记录或身份验证非常有用。
8 REMOTE_HOST 发出请求的主机的完全限定名称。如果不可用,则可以使用 REMOTE_ADDR 获取 IP 地址。
9 REQUEST_METHOD 用于发出请求的方法。最常见的方法是 GET 和 POST。
10 SCRIPT_FILENAME CGI脚本的完整路径。
11 SCRIPT_NAME CGI脚本的名称。
12 SERVER_NAME 服务器的主机名或IP地址。
13 SERVER_SOFTWARE 服务器正在运行的软件的名称和版本。

下面是一个小的CGI程序,用于列出所有的CGI变量。

#!/usr/bin/python

import os

print ("Content-type: text/html\r\n\r\n");
print ("<font size=+1>环境变量</font><\br>");
for param in os.environ.keys():
    print ("<b>%20s</b>: %s<\br>" % (param, os.environ[param]))

GET和POST方法

您可能遇到许多情况,需要将某些信息从您的浏览器传递到Web服务器,最终传递到您的CGI程序。浏览器最常用的两种方法是GET方法和POST方法。

使用GET方法传递信息

GET方法将编码的用户信息附加到页请求。页和编码信息由?字符分隔,如下所示−

http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
  • GET方法是从浏览器向Web服务器传递信息的默认方法,在浏览器的Location:框中产生一个长字符串。

  • 如果您要传递密码或其他敏感信息到服务器,则永远不要使用GET方法。

  • GET方法有大小限制:请求字符串中只能发送1024个字符。

  • GET方法使用QUERY_STRING头发送信息,并可以通过QUERY_STRING环境变量在您的CGI程序中访问它。

您可以通过简单地连接键和值对以及任何URL来传递信息,也可以使用HTML

标签使用GET方法传递信息。

简单的URL实例:GET方法

这是一个简单的URL,使用GET方法传递两个值到hello_get.py程序。

/cgi-bin/hello_get.py?first_name=ZARA&last_name=ALI

下面是处理Web浏览器提供的输入的脚本hello_get.py。我们将使用 cgi 模块,它使得访问传递的信息变得非常容易-

#!/usr/bin/python

# Import modules for CGI handling 
import cgi, cgitb 

# Create instance of FieldStorage 
form = cgi.FieldStorage() 

# Get data from fields
first_name = form.getvalue('first_name')
last_name  = form.getvalue('last_name')

print ("Content-type:text/html\r\n\r\n")
print ("<html>")
print ("<head>")
print ("<title>Hello - Second CGI Program</title>")
print ("</head>")
print ("<body>")
print ("<h2>Hello %s %s</h2>" % (first_name, last_name))
print ("</body>")
print ("</html>")

这将生成以下结果-



## Hello ZARA ALI

简单的FORM实例:GET方法

该示例使用HTML FORM和提交按钮传递两个值。我们使用相同的CGI脚本hello_get.py来处理此输入。

<form action = "/cgi-bin/hello_get.py" method = "get">
   First Name: <input type = "text" name = "first_name"> <br />

   Last Name: <input type = "text" name = "last_name" />
   <input type = "submit" value = "Submit" />
</form>

这是上述表单的实际输出,您输入名字和姓氏,然后单击提交按钮以查看结果。

First Name: Last Name:

使用POST方法传递信息

一种通常更可靠的向CGI程序传递信息的方法是POST方法。这将信息以与GET方法完全相同的方式打包,但是,它不是在URL中在?之后以文本字符串发送,而是以单独的消息发送。此消息以标准输入的形式进入CGI脚本。

下面是同样的hello_get.py脚本,它处理GET和POST方法。

#!/usr/bin/python

# Import modules for CGI handling 
import cgi, cgitb 

# Create instance of FieldStorage 
form = cgi.FieldStorage() 

# Get data from fields
first_name = form.getvalue('first_name')
last_name  = form.getvalue('last_name')

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Hello - Second CGI Program</title>"
print "</head>"
print "<body>"
print "<h2>Hello %s %s</h2>" % (first_name, last_name)
print "</body>"
print "</html>"

让我们再次以上面的相同示例为例,使用HTML FORM和提交按钮传递两个值。 我们使用相同的CGI脚本hello_get.py来处理此输入。

<form action = "/cgi-bin/hello_get.py" method = "post">
名字:<input type = "text" name = "first_name"><br />
姓氏:<input type = "text" name = "last_name" />

<input type = "submit" value = "提交" />
</form>

以下是上述表单的实际输出。 您输入名字和姓氏,然后单击提交按钮以查看结果。

名字: 姓氏:

将复选框数据传递给CGI程序

复选框用于需要选择多个选项的情况。

以下是带有两个复选框的表单的示例HTML代码 –

<form action = "/cgi-bin/checkbox.cgi" method = "POST" target = "_blank">
   <input type = "checkbox" name = "maths" value = "on" /> 数学
   <input type = "checkbox" name = "physics" value = "on" /> 物理
   <input type = "submit" value = "选择科目" />
</form>

此代码的结果是以下表单:

数学 物理

以下是checkbox.cgi脚本,用于处理Web浏览器为复选框按钮提供的输入。

#!/usr/bin/python

# 导入用于CGI处理的模块
import cgi, cgitb 

# 创建FieldStorage实例
form = cgi.FieldStorage() 

# 从字段获取数据
if form.getvalue('maths'):
   math_flag = "ON"
else:
   math_flag = "OFF"

if form.getvalue('physics'):
   physics_flag = "ON"
else:
   physics_flag = "OFF"

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Checkbox - Third CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> 复选框 数学 : %s</h2>" % math_flag
print "<h2> 复选框 物理 : %s</h2>" % physics_flag
print "</body>"
print "</html>"

将单选按钮数据传递给CGI程序

当只需要选择一个选项时,使用单选按钮。

以下是带有两个单选按钮的表单的示例HTML代码 –

<form action = "/cgi-bin/radiobutton.py" method = "post" target = "_blank">
   <input type = "radio" name = "subject" value = "maths" /> 数学
   <input type = "radio" name = "subject" value = "physics" /> 物理
   <input type = "submit" value = "选择科目" />
</form>

此代码的结果是以下表单 –

数学 物理

以下是用于处理Web浏览器为单选按钮提供的输入的radiobutton.py脚本 –

#!/usr/bin/python

# 导入用于CGI处理的模块
import cgi, cgitb 

# 创建FieldStorage实例
form = cgi.FieldStorage() 

# 从字段获取数据
if form.getvalue('subject'):
   subject = form.getvalue('subject')
else:
   subject = "未选择"

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Radio - Fourth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2>所选的科目是 %s</h2>" % subject
print "</body>"
print "</html>"

将文本区域数据传递给CGI程序

当需要传送多行文本到CGI程序时,使用TEXTAREA元素。

以下是具有TEXTAREA框的表单的示例HTML代码−

<form action = "/cgi-bin/textarea.py" method = "post" target = "_blank">
   <textarea name = "textcontent" cols = "40" rows = "4">
      在此处输入文本...
   </textarea>
   <input type = "submit" value = "提交" />
</form>

此代码的结果是以下表单−

在此处输入文本…

下面是用于处理Web浏览器提供的输入的textarea.cgi脚本−

#!/usr/bin/python

# 导入处理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\r\n\r\n"
print "<html>"
print "<head>";
print "<title>文本区域-第五个CGI程序</title>"
print "</head>"
print "<body>"
print "<h2> 输入的文本内容为 %s</h2>" % text_content
print "</body>"

向CGI程序传递下拉框数据

当我们有许多可用选项但只选择一两个时,使用下拉框。

以下是具有一个下拉框的表单的示例HTML代码−

<form action = "/cgi-bin/dropdown.py" method = "post" target = "_blank">
   <select name = "dropdown">
      <option value = "Maths" selected>数学</option>
      <option value = "Physics">物理</option>
   </select>
   <input type = "submit" value = "提交"/>
</form>

此代码的结果是以下表单−

数学 物理

下面是处理Web浏览器提供的输入的dropdown.py脚本。

#!/usr/bin/python

# 导入处理CGI的模块
import cgi, cgitb 

# 创建FieldStorage实例
form = cgi.FieldStorage() 

# 从字段获取数据
if form.getvalue('dropdown'):
   subject = form.getvalue('dropdown')
else:
   subject = "未输入"

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>下拉框-第六个CGI程序</title>"
print "</head>"
print "<body>"
print "<h2> 选中的科目为 %s</h2>" % subject
print "</body>"
print "</html>"

在CGI中使用Cookies

HTTP协议是一种无状态协议。对于商业网站,需要在不同页面之间保持会话信息。例如,一个用户注册需要完成许多页面。如何在所有网页上维护用户的会话信息?

在许多情况下,使用Cookies是记住和跟踪偏好、购买、佣金和其他信息的最有效方法,这些信息有利于更好的访问者体验或站点统计。

它是如何工作的?

您的服务器以cookie的形式向访问者的浏览器发送一些数据。浏览器可能接受cookie。如果接受,则以纯文本记录的形式存储在访问者的硬盘驱动器上。现在,当访问者到达您站点的另一页时,cookie可供检索。一旦检索到,您的服务器就知道/记得存储了什么。

Cookies是一个5个可变长度字段的纯文本数据记录−

  • 过期时间(Expires) − cookie 将失效的时间。如果这个值为空,那么 cookie 将在浏览器关闭时失效。

  • 域名(Domain) − 网站的域名。

  • 路径(Path) − 设置 cookie 的目录或者网页的路径。如果你想要从任何目录或者页面检索 cookie,则可以设置为空。

  • 安全性(Secure) − 如果这个字段包含“secure”这个词,那么 cookie 只能通过安全服务器获取。如果这个字段为空,那么没有这样的限制。

  • 名称 = 值(Name = Value) − cookie 是通过键值对的形式进行设置和检索的。

设置 Cookie

向浏览器发送 cookie 非常容易。在 Content-type 字段之前,这些 cookie 会随着 HTTP Header 一起发送。假设你想要将 UserID 和 Password 设置为 cookie。要设置 cookie,可以像下面这样做 −

#!/usr/bin/python
print "Set-Cookie:UserID = XYZ;\r\n"
print "Set-Cookie:Password = XYZ123;\r\n"
print "Set-Cookie:Expires = Tuesday, 31-Dec-2007 23:12:40 GMT;\r\n"
print "Set-Cookie:Domain = www.tutorialspoint.com;\r\n"
print "Set-Cookie:Path = /perl;\n"
print "Content-type:text/html\r\n\r\n"
...........HTML 内容剩余部分....

通过这个例子,你应该已经了解了如何设置 cookie。我们使用 Set-Cookie HTTP header 来设置 cookie。

设置 cookie 的属性像 Expires、Domain 和 Path 是可选的。需注意在发送 magic line”Content-type:text/html\r\n\r\n” 之前,cookie 已经被设置。

检索 Cookie

检索所有的 cookie 及其值非常容易。这些 cookie 存储在 CGI 环境变量 HTTP_COOKIE 中,格式如下所示 −

key1 = value1;key2 = value2;key3 = value3....

这里是如何检索 cookie 的示例。

#!/usr/bin/python

# 导入处理 CGI 的模块 
from os import environ
import cgi, cgitb

if environ.has_key('HTTP_COOKIE'):
   for cookie in map(strip, split(environ['HTTP_COOKIE'], ';')):
      (key, value ) = split(cookie, '=');
      if key == "UserID":
         user_id = value

      if key == "Password":
         password = value

print "User ID  = %s" % user_id
print "Password = %s" % password

以下是上述脚本设置的 cookie 的结果 −

User ID = XYZ
Password = XYZ123

文件上传示例

要上传文件,HTML 表单必须具有 enctype 属性,设置为“multipart/form-data”。文件类型的输入标记会创建一个“浏览”按钮。

<html>
   <body>
      <form enctype = "multipart/form-data" action = "save_file.py" method = "post">
      <p>文件:<input type = "file" name = "filename" /></p>
      <p><input type = "submit" value = "上传" /></p>
      </form>
   </body>
</html>

此代码的结果是以下表格 −

文件:

上述示例已被故意禁用,以防止在我们的服务器上上传文件,但是你可以在自己的服务器上尝试上面的代码。

下面是处理文件上传的脚本 save_file.py

#!/usr/bin/python

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>
   <body>
      <p>%s</p>
   </body>
</html>
""" % (message,)

如果你在Unix/Linux上运行上述脚本,则需要考虑将文件分隔符替换为以下内容,否则在你的Windows机器上,上述open()语句应该能够正常工作。

fn = os.path.basename(fileitem.filename.replace("\\", "/" ))

如何弹出“文件下载”对话框?

有时,需要提供一个选项,用户可以点击链接,它会弹出一个“文件下载”对话框,而不是显示实际内容。这很容易通过HTTP标题实现。此HTTP标头与先前部分中提到的标头不同。

例如,如果您想从给定的链接中将一个 FileName 文件设置为可下载文件,则其语法如下 −

#!/usr/bin/python

# HTTP标题
print " **Content-Type:** application/octet-stream; name = \"FileName\"\r\n";
print " **Content-Disposition:** attachment; filename = \"FileName\"\r\n\n";

# 实际文件内容将放在这里。
fo = open("foo.txt", "rb")

str = fo.read();
print str

# 关闭文件
fo.close()

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程