Python 简明教程
Python - CGI Programming
通用网关接口 (CGI) 是一组标准,定义了 Web 服务器和自定义脚本来交换信息的规则。CGI 规范目前由 NCSA 负责维护。
The Common Gateway Interface, or CGI, is a set of standards that define how information is exchanged between the web server and a custom script. The CGI specs are currently maintained by the NCSA.
What is CGI?
-
The Common Gateway Interface, or CGI, is a standard for external gateway programs to interface with information servers such as HTTP servers.
-
The current version is CGI/1.1 and CGI/1.2 is under progress.
Web Browsing
为了理解 CGI 的概念,让我们来看看当我们点击超链接来浏览特定的网页或 URL 时会发生什么。
To understand the concept of CGI, let us see what happens when we click a hyper link to browse a particular web page or URL.
-
Your browser contacts the HTTP web server and demands for the URL, i.e., filename.
-
Web Server parses the URL and looks for the filename. If it finds that file then sends it back to the browser, otherwise sends an error message indicating that you requested a wrong file.
-
Web browser takes response from web server and displays either the received file or error message.
但是,可以设置 HTTP 服务器,以便只要请求特定目录中的文件,就不会发回该文件;而是将其作为程序执行,并且该程序输出的任何内容都会发回以供浏览器显示。此功能称为通用网关接口或 CGI,并且这些程序称为 CGI 脚本。这些 CGI 程序可以是 Python 脚本、PERL 脚本、Shell 脚本、C 或 C++ 程序等。
However, it is possible to set up the HTTP server so that whenever a file in a certain directory is requested that file is not sent back; instead it is executed as a program, and whatever that program outputs is sent back for your browser to display. This function is called the Common Gateway Interface or CGI and the programs are called CGI scripts. These CGI programs can be a Python Script, PERL Script, Shell Script, C or C++ program, etc.
Web Server Support and Configuration
在继续进行 CGI 编程之前,请确保你的网页服务器支持 CGI,并且它配置为处理 CGI 程序。所有要由 HTTP 服务器执行的 CGI 程序都保存在预先配置的目录中。此目录称为 CGI 目录,按惯例将其命名为 /var/www/cgi-bin。按惯例,CGI 文件的扩展名为 cgi, ,但你也可以使用 python 扩展名 .py 保留你的文件。
Before you proceed with CGI Programming, make sure that your Web Server supports CGI and it is configured to handle CGI Programs. All the CGI Programs to be executed by the HTTP server are kept in a pre-configured directory. This directory is called CGI Directory and by convention it is named as /var/www/cgi-bin. By convention, CGI files have extension as. cgi, but you can keep your files with python extension .py as well.
默认情况下,Linux 服务器配置为仅运行 /var/www 中 cgi-bin 目录中的脚本。如果你想指定任何其他目录来运行 CGI 脚本,请在 httpd.conf 文件中注释掉以下行:
By default, the Linux server is configured to run only the scripts in the cgi-bin directory in /var/www. If you want to specify any other directory to run your CGI scripts, comment the following lines in the httpd.conf file −
<Directory "/var/www/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
<Directory "/var/www/cgi-bin">
Options All
</Directory>
还应添加以下行为 apache 服务器添加,以将 .py 文件视为 cgi 脚本。
The following line should also be added for apache server to treat .py file as cgi script.
AddHandler cgi-script .py
在此,我们假设你的网页服务器已成功启动并运行,并且你可以运行任何其他 CGI 程序,如 Perl 或 Shell 等。
Here, we assume that you have Web Server up and running successfully and you are able to run any other CGI program like Perl or Shell, etc.
First CGI Program
这里有一个简单的链接,它链接到一个名为 hello.py 的 CGI 脚本。此文件保存在 /var/www/cgi-bin 目录中,其内容如下。在运行 CGI 程序之前,请确保使用 chmod 755 hello.py UNIX 命令更改文件的模式,使其可执行。
Here is a simple link, which is linked to a CGI script called hello.py. This file is kept in /var/www/cgi-bin directory and it has following content. Before running your CGI program, make sure you have change mode of file using chmod 755 hello.py UNIX command to make file executable.
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>')
Note − 脚本中的第一行必须是 Python 可执行文件的路径。它在 Python 程序中显示为注释,但它被称为井号行。
Note − First line in the script must be the path to Python executable. It appears as a comment in Python program, but it is called shebang line.
在 Linux 中,它应该是 #!/usr/bin/python3。
In Linux, it should be #!/usr/bin/python3.
在 Windows 中,它应该是 #!c:/python311/python.exd。
In Windows, it should be #!c:/python311/python.exd.
在浏览器中输入以下 URL:
Enter the following URL in your browser −
http://localhost/cgi-bin/hello.py
Hello Word! This is my first CGI program
此 hello.py 脚本是一个简单的 Python 脚本,它在 STDOUT 文件(即屏幕)上写入其输出。有一个重要且额外的可用功能,即第一个要打印的行 Content-type:text/html\r\n\r\n 。此行发回浏览器,它指定要在浏览器屏幕上显示的内容类型。
This hello.py script is a simple Python script, which writes its output on STDOUT file, i.e., screen. There is one important and extra feature available which is first line to be printed Content-type:text/html\r\n\r\n. This line is sent back to the browser and it specifies the content type to be displayed on the browser screen.
现在你肯定已经理解了 CGI 的基本概念,并且你可以使用 Python 编写许多复杂的 CGI 程序。此脚本还可以与任何其他外部系统交互,以交换诸如 RDBMS 之类的信息。
By now you must have understood basic concept of CGI and you can write many complicated CGI programs using Python. This script can interact with any other external system also to exchange information such as RDBMS.
HTTP Header
行 Content-type:text/html\r\n\r\n 是发送到浏览器的 HTTP 头的一部分,用于理解内容。所有 HTTP 头都将采用以下形式:
The line Content-type:text/html\r\n\r\n is part of HTTP header which is sent to the browser to understand the content. All the HTTP header will be in the following form −
HTTP Field Name: Field Content
For Example
Content-type: text/html\r\n\r\n
有其他一些重要的 HTTP 头信息,您将在 CGI 编程中经常使用。
There are few other important HTTP headers, which you will use frequently in your CGI Programming.
CGI Environment Variables
所有的 CGI 程序都可以访问以下环境变量。这些变量在编写任何 CGI 程序时都扮演着重要的角色。
All the CGI programs have access to the following environment variables. These variables play an important role while writing any CGI program.
这里有一个列出所有 CGI 变量的小型 CGI 程序。单击此链接以查看结果 @ {s0}
Here is small CGI program to list out all the CGI variables. Click this link to see the result Get Environment
import os
print ("Content-type: text/html\r\n\r\n");
print ("<font size=+1>Environment</font><\br>");
for param in os.environ.keys():
print ("<b>%20s</b>: %s<\br>" % (param, os.environ[param]))
GET and POST Methods
当您需要将一些信息从浏览器传递到 Web 服务器以及最终传递到 CGI 程序时,您肯定碰到过很多情况。浏览器通常使用两种方法向 Web 服务器传递此信息。这些方法分别是 GET 方法和 POST 方法。
You must have come across many situations when you need to pass some information from your browser to web server and ultimately to your CGI Program. Most frequently, browser uses two methods two pass this information to web server. These methods are GET Method and POST Method.
Passing Information using GET method
GET 方法将编码后的用户信息附加到页面请求后发送。页面和编码后的信息由 ? 字符分隔,如下所示:
The GET method sends the encoded user information appended to the page request. The page and the encoded information are separated by the ? character as follows −
http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
-
The GET method is the default method to pass information from the browser to the web server and it produces a long string that appears in your browser’s Location:box.
-
Never use GET method if you have password or other sensitive information to pass to the server.
-
The GET method has size limtation: only 1024 characters can be sent in a request string.
-
The GET method sends information using QUERY_STRING header and will be accessible in your CGI Program through QUERY_STRING environment variable.
您可以通过简单地连接键和值对以及任意 URL 来传递信息,或者可以使用 HTML <FORM> 标记使用 GET 方法传递信息。
You can pass information by simply concatenating key and value pairs along with any URL or you can use HTML <FORM> tags to pass information using GET method.
Simple URL Example:Get Method
这是一个简单的 URL,它使用 GET 方法将两个值传递到 hello_get.py 程序。
Here is a simple URL, which passes two values to hello_get.py program using GET method.
/cgi-bin/hello_get.py?first_name=Malhar&last_name=Lathkar
以下是用于处理 Web 浏览器给出的输入的 hello_get.py 脚本。我们将使用 CGI 模块,它使得访问传递的信息变得非常容易。
Given below is the hello_get.py script to handle the input given by web browser. We are going to use the cgi module, which makes it very easy to access the passed information −
# 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")
print()
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>')
这将生成以下结果:
This would generate the following result −
Hello Malhar Lathkar
Simple FORM Example:GET Method
此示例使用 HTML 表单和提交按钮传递两个值。我们使用相同的 CGI 脚本 hello_get.py 来处理此输入。
This example passes two values using HTML FORM and submit button. We use same CGI script hello_get.py to handle this input.
<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>
这是上述表单的实际输出,您输入名字和姓氏,然后单击提交按钮查看结果。
Here is the actual output of the above form, you enter First and Last Name and then click submit button to see the result.
Passing Information Using POST Method
向 CGI 程序传递信息的一个通常更为可靠的方法是 POST 方法。此方法将信息打包的方式与 GET 方法完全相同,但是不是在 URL 中的 ? 之后作为文本字符串发送信息,而是作为单独的消息发送信息。此消息以标准输入的形式进入 CGI 脚本。
A generally more reliable method of passing information to a CGI program is the POST method. This packages the information in exactly the same way as GET methods, but instead of sending it as a text string after a ? in the URL it sends it as a separate message. This message comes into the CGI script in the form of the standard input.
以下相同的 hello_get.py 脚本同时处理 GET 和 POST 方法。
Below is same hello_get.py script which handles GET as well as POST method.
# 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 表单和提交按钮传递两个值。我们使用相同的 CGI 脚本 hello_get.py 来处理此输入。
Let us take again same example as above which passes two values using HTML FORM and submit button. We use same CGI script hello_get.py to handle this input.
<form action = "/cgi-bin/hello_get.py" method = "post">
First Name: <input type = "text" name = "first_name"><br />
Last Name: <input type = "text" name = "last_name" />
<input type = "submit" value = "Submit" />
</form>
这是上述表单的实际输出。您输入名字和姓氏,然后单击提交按钮查看结果。
Here is the actual output of the above form. You enter First and Last Name and then click submit button to see the result.
Passing Checkbox Data to CGI Program
如果要求选择多个选项,则使用复选框。
Checkboxes are used when more than one option is required to be selected.
这是一个包含两个复选框的表单的示例 HTML 代码−
Here is example HTML code for a form with two checkboxes −
<form action = "/cgi-bin/checkbox.cgi" method = "POST" target = "_blank">
<input type = "checkbox" name = "maths" value = "on" /> Maths
<input type = "checkbox" name = "physics" value = "on" /> Physics
<input type = "submit" value = "Select Subject" />
</form>
这段代码的结果是以下表单−
The result of this code is the following form −
以下是 checkbox.cgi 脚本,用于处理由网络浏览器针对复选框按钮提供的输入。
Below is checkbox.cgi script to handle input given by web browser for checkbox button.
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
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> CheckBox Maths is : %s</h2>" % math_flag
print "<h2> CheckBox Physics is : %s</h2>" % physics_flag
print "</body>"
print "</html>"
Passing Radio Button Data to CGI Program
如果要求只选择一个选项,则使用单选按钮。
Radio Buttons are used when only one option is required to be selected.
这是一个包含两个单选按钮的表单的示例 HTML 代码−
Here is example HTML code for a form with two radio buttons −
<form action = "/cgi-bin/radiobutton.py" method = "post" target = "_blank">
<input type = "radio" name = "subject" value = "maths" /> Maths
<input type = "radio" name = "subject" value = "physics" /> Physics
<input type = "submit" value = "Select Subject" />
</form>
这段代码的结果是以下表单−
The result of this code is the following form −
下方是 radiobutton.py 脚本,它用于处理由 Web 浏览器针对单选按钮提供的输入 −
Below is radiobutton.py script to handle input given by web browser for radio button −
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
if form.getvalue('subject'):
subject = form.getvalue('subject')
else:
subject = "Not set"
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> Selected Subject is %s</h2>" % subject
print "</body>"
print "</html>"
Passing Text Area Data to CGI Program
如果必须将多行文本传递给 CGI 程序,则使用 TEXTAREA 元素。
TEXTAREA element is used when multiline text has to be passed to the CGI Program.
这是一个包含一个 TEXTAREA 框的表单的示例 HTML 代码−
Here is example HTML code for a form with a TEXTAREA box −
<form action = "/cgi-bin/textarea.py" method = "post" target = "_blank">
<textarea name = "textcontent" cols = "40" rows = "4">
Type your text here...
</textarea>
<input type = "submit" value = "Submit" />
</form>
这段代码的结果是以下表单−
The result of this code is the following form −
以下是 textarea.cgi 脚本,它用于处理由 Web 浏览器提供的输入 −
Below is textarea.cgi script to handle input given by web browser −
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
if form.getvalue('textcontent'):
text_content = form.getvalue('textcontent')
else:
text_content = "Not entered"
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>";
print "<title>Text Area - Fifth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Entered Text Content is %s</h2>" % text_content
print "</body>"
Passing Drop Down Box Data to CGI Program
当我们有许多可用选项,但只选择一个或两个时,可以使用下拉框。
Drop Down Box is used when we have many options available but only one or two will be selected.
这是一个包含一个下拉框的表单的示例 HTML 代码−
Here is example HTML code for a form with one drop down box −
<form action = "/cgi-bin/dropdown.py" method = "post" target = "_blank">
<select name = "dropdown">
<option value = "Maths" selected>Maths</option>
<option value = "Physics">Physics</option>
</select>
<input type = "submit" value = "Submit"/>
</form>
这段代码的结果是以下表单−
The result of this code is the following form −
以下是 dropdown.py 脚本,它用于处理由 Web 浏览器提供的输入。
Below is dropdown.py script to handle input given by web browser.
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
if form.getvalue('dropdown'):
subject = form.getvalue('dropdown')
else:
subject = "Not entered"
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Dropdown Box - Sixth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Selected Subject is %s</h2>" % subject
print "</body>"
print "</html>"
Using Cookies in CGI
HTTP 协议是无状态协议。对于商业网站来说,必须在不同的页面之间保持会话信息。例如,一个用户注册在完成多个页面后结束。如何跨所有网页维护用户的会话信息?
HTTP protocol is a stateless protocol. For a commercial website, it is required to maintain session information among different pages. For example, one user registration ends after completing many pages. How to maintain user’s session information across all the web pages?
在许多情况下,使用 cookie 是记忆和跟踪偏好、购买、佣金和其他信息最有效的方法,而这些信息对于提升访问者体验或网站统计至关重要。
In many situations, using cookies is the most efficient method of remembering and tracking preferences, purchases, commissions, and other information required for better visitor experience or site statistics.
How It Works?
您的服务器会以 Cookie 的形式向访客的浏览器发送一些数据。浏览器可能会接受 Cookie。如果接受,它将作为纯文本记录存储在访客硬盘上。现在,当访客到达您站点上的另一个页面时,可以检索 Cookie。检索后,您的服务器知道/记住存储的内容。
Your server sends some data to the visitor’s browser in the form of a cookie. The browser may accept the cookie. If it does, it is stored as a plain text record on the visitor’s hard drive. Now, when the visitor arrives at another page on your site, the cookie is available for retrieval. Once retrieved, your server knows/remembers what was stored.
Cookie 是纯文本数据记录,包含 5 个可变长度字段:
Cookies are a plain text data record of 5 variable-length fields −
-
Expires − The date the cookie will expire. If this is blank, the cookie will expire when the visitor quits the browser.
-
Domain − The domain name of your site.
-
Path − The path to the directory or web page that sets the cookie. This may be blank if you want to retrieve the cookie from any directory or page.
-
Secure − If this field contains the word "secure", then the cookie may only be retrieved with a secure server. If this field is blank, no such restriction exists.
-
Name = Value − Cookies are set and retrieved in the form of key and value pairs.
Setting up Cookies
将 Cookie 发送到浏览器非常容易。这些 Cookie 与内容类型字段之前的 HTTP 标头一起发送。假设您希望将 UserID 和密码设置为 Cookie。设置 Cookie 如下所示 −
It is very easy to send cookies to browser. These cookies are sent along with HTTP Header before to Content-type field. Assuming you want to set UserID and Password as cookies. Setting the cookies is done as follows −
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"
...........Rest of the HTML Content....
从这个示例中,您应该已经了解了如何设置 Cookie。我们使用 Set-Cookie HTTP 标头设置 Cookie。
From this example, you must have understood how to set cookies. We use Set-Cookie HTTP header to set cookies.
设置 Expires、Domain 和 Path 等 cookie 属性是可选的。需要注意的是,在发送 magci 行 "Content-type:text/html\r\n\r\n 之前设置 cookie。
It is optional to set cookies attributes like Expires, Domain, and Path. It is notable that cookies are set before sending magic line "Content-type:text/html\r\n\r\n.
Retrieving Cookies
检索所有设置的 cookie 非常容易。Cookie 存储在 CGI 环境变量 HTTP_COOKIE 中,它们将具有以下形式:
It is very easy to retrieve all the set cookies. Cookies are stored in CGI environment variable HTTP_COOKIE and they will have following form −
key1 = value1;key2 = value2;key3 = value3....
以下是有关如何检索 cookie 的示例。
Here is an example of how to retrieve cookies.
# Import modules for CGI handling
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 产生以下结果:
This produces the following result for the cookies set by above script −
User ID = XYZ
Password = XYZ123
File Upload Example
要上传文件,HTML 表单必须将 enctype 属性设置为 multipart/form-data 。具有 file 类型的 input 标记创建一个“浏览”按钮。
To upload a file, the HTML form must have the enctype attribute set to multipart/form-data. The input tag with the file type creates a "Browse" button.
<html>
<body>
<form enctype = "multipart/form-data" action = "save_file.py" method = "post">
<p>File: <input type = "file" name = "filename" /></p>
<p><input type = "submit" value = "Upload" /></p>
</form>
</body>
</html>
这段代码的结果是以下表单−
The result of this code is the following form −
上述示例已故意禁用,以防止用户在我们的服务器上上传文件,但是您可以在自己的服务器上尝试上述代码。
Above example has been disabled intentionally to save people uploading file on our server, but you can try above code with your server.
以下是处理文件上传的脚本 save_file.py :
Here is the script save_file.py to handle file upload −
import cgi, os
import cgitb; cgitb.enable()
form = cgi.FieldStorage()
# Get filename here.
fileitem = form['filename']
# Test if the file was uploaded
if fileitem.filename:
# strip leading path from file name to avoid
# directory traversal attacks
fn = os.path.basename(fileitem.filename)
open('/tmp/' + fn, 'wb').write(fileitem.file.read())
message = 'The file "' + fn + '" was uploaded successfully'
else:
message = 'No file was uploaded'
print """\
Content-Type: text/html\n
<html>
<body>
<p>%s</p>
</body>
</html>
""" % (message,)
如果您在 Unix/Linux 上运行上述脚本,则需要小心更换文件分隔符,如下所示,否则在您的 Windows 机器上,上述 open() 语句应正常工作。
If you run the above script on Unix/Linux, then you need to take care of replacing file separator as follows, otherwise on your windows machine above open() statement should work fine.
fn = os.path.basename(fileitem.filename.replace("\\", "/" ))
How To Raise a "File Download" Dialog Box?
有时,您希望提供一个选项,用户可以单击一个链接,并向用户弹出“文件下载”对话框,而不是显示实际内容。这非常容易,可以通过 HTTP 头来实现。这个 HTTP 头与前一部分中提到的头不同。
Sometimes, it is desired that you want to give option where a user can click a link and it will pop up a "File Download" dialogue box to the user instead of displaying actual content. This is very easy and can be achieved through HTTP header. This HTTP header is be different from the header mentioned in previous section.
例如,如果您希望从给定链接下载 FileName 文件,则其语法如下:
For example, if you want make a FileName file downloadable from a given link, then its syntax is as follows −
# HTTP Header
print "Content-Type:application/octet-stream; name = \"FileName\"\r\n";
print "Content-Disposition: attachment; filename = \"FileName\"\r\n\n";
# Actual File Content will go here.
fo = open("foo.txt", "rb")
str = fo.read();
print str
# Close opend file
fo.close()