第 5 章 电子邮件协议、FTP和CGI编程
本章攻略:
- 列出远程FTP服务器中的文件
- 把本地文件上传到远程FTP服务器中
- 把当前工作目录中的内容压缩成ZIP文件后通过电子邮件发送
- 通过POP3协议下载谷歌电子邮件
- 通过IMAP协议查收远程服务器中的电子邮件
- 通过Gmail的SMTP服务器发送带有附件的电子邮件
- 使用CGI为基于Python的Web服务器编写一个留言板
5.1 简介
本章通过Python攻略介绍FTP、电子邮件和CGI通信协议。Python这门语言很高效也很友好。使用Python可以很方便地实现简单的FTP操作,例如下载和上传文件。
本章有些有趣的攻略,例如在Python脚本中管理谷歌电子邮件(也叫Gmail)。使用这些攻略可以通过IMAP、POP3和SMTP协议查收、下载和发送电子邮件。还有一个攻略编写了一个支持CGI功能的Web服务器,演示了基本的CGI操作,例如编写Web应用中的游客留言表单。
5.2 列出FTP远程服务器中的文件
你可能想列出Linux内核FTP网站(ftp.kernel.org)中的文件。这个攻略也可用在其他FTP网站上。
5.2.1 准备工作
如果处理需要账户的FTP网站,你需要提供用户名和密码。但在这个攻略中不需要Linux内核FTP网站的用户名和密码,因为可以匿名登录。
5.2.2 实战演练
要从选中的FTP网站中获取文件,可以使用ftplib
库。这个库的详细文档可在http://docs.python.org/2/library/ftplib.html中查看。
我们来看一下如何使用ftplib
获取文件。
代码清单5-1是一次简单的FTP连接测试,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
FTP_SERVER_URL = 'ftp.kernel.org'
import ftplib
def test_ftp_connection(path, username, email):
#Open ftp connection
ftp = ftplib.FTP(path, username, email)
#List the files in the pub directory
ftp.cwd("pub")
print "File list at %s:" %path
files = ftp.dir()
print files
ftp.quit()
if __name__ == '__main__':
test_ftp_connection(path=FTP_SERVER_URL, username='anonymous',
email='nobody@nourl.com')
这个攻略会列出FTP路径(ftp.kernel.org/pub)中的文件和文件夹。运行这个脚本后,会看到如下输出:
$ python 5_1_list_files_on_ftp_server.py
File list at ftp.kernel.org:
drwxrwxr-x 6 ftp ftp 4096 Dec 01 2011 dist
drwxr-xr-x 13 ftp ftp 4096 Nov 16 2011 linux
drwxrwxr-x 3 ftp ftp 4096 Sep 23 2008 media
drwxr-xr-x 17 ftp ftp 4096 Jun 06 2012 scm
drwxrwxr-x 2 ftp ftp 4096 Dec 01 2011 site
drwxr-xr-x 13 ftp ftp 4096 Nov 27 2011 software
drwxr-xr-x 3 ftp ftp 4096 Apr 30 2008 tools
5.2.3 原理分析
这个攻略使用ftplib
创建了一个连接到ftp.kernel.org/pub上的FTP客户端会话。test_ftp_connection()
函数的参数是连接FTP服务器所需的路径、用户名和电子邮件地址。
FTP客户端会话使用ftplib
库中的FTP()
函数创建,其参数就是传入test_ftp_connection()
的参数。FTP()
函数返回一个客户端句柄,用于运行常用的FTP命令,例如切换工作目录的命令cwd()
。dir()
方法返回目录的文件夹结构。
处理完成后,最后调用ftp.quit()
终止FTP会话。
5.3 把本地文件上传到远程FTP服务器中
你可能想把文件上传到FTP服务器中。
5.3.1 准备工作
让我们来搭建一个本地FTP服务器。在Unix/Linux中,可以使用下述命令安装wu-ftpd包:
$ sudo apt-get install wu-ftpd
在运行Windows的设备中,可以安装FileZilla FTP服务器,下载地址为https://filezilla-project.org/download.php?type=server。
你应该按照FTP服务器包的说明来创建一个FTP账户。
你可能还想上传一个文件到FTP服务器中。你可以指定服务器地址、登录凭据和文件名作为脚本的输入参数。你应该在本地新建一个文件,命名为readme.txt,在里面随便写些文本。
5.3.2 实战演练
我们使用下面的脚本来搭建一个FTP本地服务器。在Unix/Linux中,可以安装wu-ftpd包。然后,可以把文件上传到已登录用户的家目录中。你可以指定服务器地址、登录凭据和文件名作为脚本的输入参数。
代码清单5-2是FTP上传文件示例,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import os
import argparse
import ftplib
import getpass
LOCAL_FTP_SERVER = 'localhost'
LOCAL_FILE = 'readme.txt'
def ftp_upload(ftp_server, username, password, file_name):
print "Connecting to FTP server: %s" %ftp_server
ftp = ftplib.FTP(ftp_server)
print "Login to FTP server: user=%s" %username
ftp.login(username, password)
ext = os.path.splitext(file_name)[1]
if ext in (".txt", ".htm", ".html"):
ftp.storlines("STOR " + file_name, open(file_name))
else:
ftp.storbinary("STOR " + file_name, open(file_name, "rb"), 1024)
print "Uploaded file: %s" %file_name
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='FTP Server Upload Example')
parser.add_argument('--ftp-server', action="store", dest="ftp_server", default=LOCAL_FTP_SERVER)
parser.add_argument('--filename', action="store", dest="file_name", default=LOCAL_FILE)
parser.add_argument('--username', action="store", dest="username", default=getpass.getuser())
given_args = parser.parse_args()
ftp_server, file_name, username = given_args.ftp_server, given_args.file_name, given_args.username
password = getpass.getpass(prompt="Enter you FTP password: ")
ftp_upload(ftp_server, username, password, file_name)
如果搭建了本地FTP服务器,运行下面这个脚本后会登入FTP服务器并上传文件。如果命令行中没有指定默认文件名,这个脚本将上传readme.txt文件。
$ python 5_2_upload_file_to_ftp_server.py
Enter your FTP password:
Connecting to FTP server: localhost
Login to FTP server: user=faruq
Uploaded file: readme.txt
$ cat homefaruq/readme.txt
This file describes what to do with the .bz2 files you see elsewhere
on this site (ftp.kernel.org).
5.3.3 原理分析
在这个攻略中,我们假设本地FTP服务器正在运行中。除此之外还可以连接远程FTP服务器。在ftp_upload()
方法中,使用Python中的ftplib
模块提供的FTP()
函数创建一个FTP连接对象。然后调用login()
方法,登录服务器。
登录成功后,在ftp
对象上调用方法storlines()
或storbinary()
执行STOR
命令。前一个方法用于发送ASCII文本文件,例如HTML或纯文本文件。后一个方法用于发送二进制数据,例如压缩文件。
这些FTP方法最好放在try-catch
错误处理块中,为了行文简洁,这里并没有这么做。
5.4 把当前工作目录中的内容压缩成ZIP文件后通过电子邮件发送
如果能把当前工作目录中的内容压缩成ZIP文件发送给别人,该多么有趣啊。使用这个攻略,你可以快速和朋友共享文件。
5.4.1 准备工作
如果你的设备中没有安装任何邮件服务器,你需要安装一个本地邮件服务器,例如postfix
。在Debian/Ubuntu系统中可以使用apt-get
的默认设置安装,如下面的命令所示:
$ sudo apt-get install postfix
5.4.2 实战演练
我们首先要压缩当前目录,然后创建一封电子邮件。电子邮件可通过外部的SMTP主机发送,也可使用本地电子邮件服务器发送。和其他的攻略类似,发件人和收件人信息都从命令行参数中获取。
代码清单5-3展示了如何把文件夹压缩成ZIP文件再通过电子邮件发送,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import os
import argparse
import smtplib
import zipfile
import tempfile
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
def email_dir_zipped(sender, recipient):
zf = tempfile.TemporaryFile(prefix='mail', suffix='.zip')
zip = zipfile.ZipFile(zf, 'w')
print "Zipping current dir: %s" %os.getcwd()
for file_name in os.listdir(os.getcwd()):
zip.write(file_name)
zip.close()
zf.seek(0)
# Create the message
print "Creating email message..."
email_msg = MIMEMultipart()
email_msg['Subject'] = 'File from path %s' %os.getcwd()
email_msg['To'] = ', '.join(recipient)
email_msg['From'] = sender
email_msg.preamble = 'Testing email from Python.\n'
msg = MIMEBase('application', 'zip')
msg.set_payload(zf.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment',
filename=os.getcwd()[-1] + '.zip')
email_msg.attach(msg)
email_msg = email_msg.as_string()
# send the message
print "Sending email message..."
try:
smtp = smtplib.SMTP('localhost')
smtp.set_debuglevel(1)
smtp.sendmail(sender, recipient, email_msg)
except Exception, e:
print "Error: %s" %str(e)
finally:
smtp.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Email Example')
parser.add_argument('--sender', action="store", dest="sender", default='you@you.com')
parser.add_argument('--recipient', action="store", dest="recipient")
given_args = parser.parse_args()
email_dir_zipped(given_args.sender, given_args.recipient)
运行这个脚本后看到的输出如下所示。因为开启了电子邮件调试模式,所以还显示了一些额外信息。
$ python 5_3_email_current_dir_zipped.py --recipient=faruq@localhost
Zipping current dir: homefaruq/Dropbox/PacktPub/pynetcookbook/
pynetcookbook_code/chapter5
Creating email message...
Sending email message...
send: 'ehlo [127.0.0.1]\r\n'
reply: '250-debian6.debian2013.com\r\n'
reply: '250-PIPELINING\r\n'
reply: '250-SIZE 10240000\r\n'
reply: '250-VRFY\r\n'
reply: '250-ETRN\r\n'
reply: '250-STARTTLS\r\n'
reply: '250-ENHANCEDSTATUSCODES\r\n'
reply: '250-8BITMIME\r\n'
reply: '250 DSN\r\n'
reply: retcode (250); Msg: debian6.debian2013.com
PIPELINING
SIZE 10240000
VRFY
ETRN
STARTTLS
ENHANCEDSTATUSCODES
8BITMIME
DSN
send: 'mail FROM:<you@you.com> size=9141\r\n'
reply: '250 2.1.0 Ok\r\n'
reply: retcode (250); Msg: 2.1.0 Ok
send: 'rcpt TO:<faruq@localhost>\r\n'
reply: '250 2.1.5 Ok\r\n'
reply: retcode (250); Msg: 2.1.5 Ok
send: 'data\r\n'
reply: '354 End data with <CR><LF>.<CR><LF>\r\n'
reply: retcode (354); Msg: End data with <CR><LF>.<CR><LF>
data: (354, 'End data with <CR><LF>.<CR><LF>')
send: 'Content-Type: multipart/mixed;
boundary="===============0388489101==...[TRUNCATED]
reply: '250 2.0.0 Ok: queued as 42D2F34A996\r\n'
reply: retcode (250); Msg: 2.0.0 Ok: queued as 42D2F34A996
data: (250, '2.0.0 Ok: queued as 42D2F34A996')
5.4.3 原理分析
为了通过电子邮件发送压缩后的文件夹,我们用到了Python中的zipfile
、smtplib
和email
三个模块。这在email_dir_zipped()
方法中实现。这个方法接收两个参数:发件人和收件人的电子邮件地址。
为了压缩文件,我们使用tempfile
模块中的TemporaryFile
类新建了一个临时文件,把这个文件的前缀设为mail
,后缀设为.zip
。然后把临时文件作为参数传给ZipFile
类的构造方法,初始化一个ZIP压缩文件对象。接着在这个压缩对象上调用write()
方法,添加当前目录中的文件。
为了发送电子邮件,我们使用email.mime.multipart
模块中的MIMEmultipart()
类创建了一个MIME为multipart
的邮件。和普通的电子邮件一样,主题、收件人和发件人信息都在电子邮件的首部中设定。
电子邮件的附件使用MIMEBase()
方法创建。我们首先设置application/zip
首部,然后在附件对象上调用set_payload()
方法。为了正确地编码消息,调用了encoders
模块中的encode_base64()
方法。add_header()
方法能帮助我们设定附件的首部。至此,附件已经准备好,可以添加到邮件中了,因此我们调用了attach()
方法。
若想发送电子邮件,要调用smtplib
模块中的SMTP
类创建一个实例。在这个实例上调用sendmail()
方法后,会利用操作系统中的程序正确地把电子邮件发送出去。发送的细节被隐藏了,不过,如果你想看到详细的过程,可以打开调试模式,如前所示。
5.4.4 参考资源
- 本节用到的Python库详情请参阅文档,地址为http://docs.python.org/2/library/smtplib.html。
5.5 通过POP3协议下载谷歌电子邮件
你可能想通过POP3协议下载谷歌(或者其他任何一个电子邮件服务提供商)账户中的电子邮件。
5.5.1 准备工作
要运行这个攻略,你需要有谷歌或其他服务提供商的电子邮件账户。
5.5.2 实战演练
我们要尝试下载用户谷歌电子邮件账户中的第一封邮件。用户名在命令行中输入,但为了保密,密码不能在命令行中指定,而是在运行脚本时输入,而且不能显示出来。
代码清单5-4展示了如何通过POP3协议下载谷歌账户中的电子邮件,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import getpass
import poplib
GOOGLE_POP3_SERVER = 'pop.googlemail.com'
def download_email(username):
mailbox = poplib.POP3_SSL(GOOGLE_POP3_SERVER, '995')
mailbox.user(username)
password = getpass.getpass(prompt="Enter your 谷歌 password: ")
mailbox.pass_(password)
num_messages = len(mailbox.list()[1])
print "Total emails: %s" %num_messages
print "Getting last message"
for msg in mailbox.retr(num_messages)[1]:
print msg
mailbox.quit()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Email Download Example')
parser.add_argument('--username', action="store", dest="username", default=getpass.getuser())
given_args = parser.parse_args()
username = given_args.username
download_email(username)
运行这个脚本后会看到类似下面的输出。为了保护隐私,输出的内容有所删减。
$ python 5_4_download_google_email_via_pop3.py --username=<USERNAME>
Enter your 谷歌 password:
Total emails: 333
Getting last message
...[TRUNCATED]
5.5.3 原理分析
这个攻略通过POP3协议从用户的谷歌账户中下载第一封邮件。在download_email()
函数中,使用poplib
模块中的POP3_SSL
类创建了一个mailbox
对象。在POP3_SSL
类的构造方法中,我们传入了谷歌POP3服务器的地址和端口号。然后在mailbox
对象上调用user()
方法,设定用户的账户。密码使用getpass
模块中的getpass()
方法以一种安全的方式从用户的输入中获取,然后传给mailbox
对象。在mailbox
对象上调用list()
方法会以一个Python列表的形式返回电子邮件。
这个脚本先显示电子邮件的数量,然后调用retr()
方法取回第一封邮件。最后,在mailbox
对象上调用quit()
方法,安全地结束连接。
5.6 通过IMAP协议查收远程服务器中的电子邮件
除了使用POP3协议之外,还可以使用IMAP协议从谷歌账户中取回电子邮件。使用这种方法,取回后邮件不会被删除。
5.6.1 准备工作
要运行这个攻略,你需要有谷歌或其他服务提供商的电子邮件账户。
5.6.2 实战演练
让我们连接到谷歌的电子邮件账户,读取第一封电子邮件。如果你没有删除,第一封电子邮件应该是来自谷歌的欢迎消息。
代码清单5-5展示了如何通过IMAP协议查收谷歌账户中的电子邮件,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import getpass
import imaplib
GOOGLE_IMAP_SERVER = 'imap.googlemail.com'
def check_email(username):
mailbox = imaplib.IMAP4_SSL(GOOGLE_IMAP_SERVER, '993')
password = getpass.getpass(prompt="Enter your 谷歌 password: ")
mailbox.login(username, password)
mailbox.select('Inbox')
typ, data = mailbox.search(None, 'ALL')
for num in data[0].split():
typ, data = mailbox.fetch(num, '(RFC822)')
print 'Message %s\n%s\n' % (num, data[0][1])
break
mailbox.close()
mailbox.logout()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Email Download Example')
parser.add_argument('--username', action="store", dest="username", default=getpass.getuser())
given_args = parser.parse_args()
username = given_args.username
check_email(username)
运行这个脚本后会看到如下输出。为了保护隐私,删减了一些信息。
$ python 5_5_check_remote_email_via_imap.py --username=<USER_NAME>
Enter your 谷歌 password:
Message 1
Received: by 10.140.142.16; Sat, 17 Nov 2007 09:26:31 -0800 (PST)
Message-ID: <...>@mail.gmail.com>
Date: Sat, 17 Nov 2007 09:26:31 -0800
From: "Gmail Team" <mail-noreply@google.com>
To: "<User Full Name>" <USER_NAME>@gmail.com>
Subject: Gmail is different. Here's what you need to know.
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_Part_7453_30339499.1195320391988"
------=_Part_7453_30339499.1195320391988
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Messages that are easy to find, an inbox that organizes itself, great
spam-fighting tools and built-in chat. Sound cool? Welcome to Gmail.
To get started, you may want to:
[TRUNCATED]
5.6.3 原理分析
上述脚本从命令行参数中获取谷歌用户名,然后调用check_email()
函数。在这个函数中,使用imaplib
模块中的IMAP4_SSL
类创建了一个mailbox
对象,实例化时传入了谷歌的IMAP服务器地址和默认的端口号。
然后,在这个函数中使用密码登录账户。密码使用getpass
模块中的getpass()
方法从用户输入中捕获。然后在mailbox
对象上调用select()
方法,选择收件箱。
在mailbox
对象上可以调用很多有用的方法,其中两个是search()
和fetch()
,在这个脚本中用来获取第一封邮件。最后,在mailbox
对象上调用close()
和logout()
方法,安全地结束IMAP连接。
5.7 通过Gmail的SMTP服务器发送带有附件的电子邮件
你也许想从谷歌的电子邮件账户中发送一封邮件到其他账户中,或许还想为这封邮件附加一个文件。
5.7.1 准备工作
要运行这个攻略,你需要有谷歌或其他服务提供商的电子邮件账户。
5.7.2 实战演练
我们可以创建一封邮件,把Python的LOGO python-logo.gif附加到邮件中,然后从谷歌账户中发给另一个账户。
代码清单5-6展示了如何从谷歌账户中发送电子邮件:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import os
import getpass
import re
import sys
import smtplib
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
def send_email(sender, recipient):
""" Send email message """
msg = MIMEMultipart()
msg['Subject'] = 'Python Emaill Test'
msg['To'] = recipient
msg['From'] = sender
subject = 'Python email Test'
message = 'Images attached.'
# attach imgae files
files = os.listdir(os.getcwd())
gifsearch = re.compile(".gif", re.IGNORECASE)
files = filter(gifsearch.search, files)
for filename in files:
path = os.path.join(os.getcwd(), filename)
if not os.path.isfile(path):
continue
img = MIMEImage(open(path, 'rb').read(), subtype="gif")
img.addheader('Content-Disposition', 'attachment', filename=filename)
msg.attach(img)
part = MIMEText('text', "plain")
part.set_payload(message)
msg.attach(part)
# create smtp session
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo
password = getpass.getpass(prompt="Enter your 谷歌 password: ")
session.login(sender, password)
session.sendmail(sender, recipient, msg.as_string())
print "Email sent."
session.quit()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Email Sending Example')
parser.add_argument('--sender', action="store", dest="sender")
parser.add_argument('--recipient', action="store", dest="recipient")
given_args = parser.parse_args()
send_email(given_args.sender, given_args.recipient)
如果提供的谷歌账户信息正确,运行这个脚本后会看到成功消息,提示把一封邮件发送给了另一个电子邮件地址。运行这个脚本之后,可以到收件人的电子邮件账户中确认是否真的成功发送了邮件。
$ python 5_6_send_email_from_gmail.py --sender=<USERNAME>@gmail.com –
recipient=<USER>@<ANOTHER_COMPANY.com>
Enter you Google password:
Email sent.
5.7.3 原理分析
在这个攻略的send_email()
函数中创建了一封邮件。我们为send_email()
函数提供了谷歌账户,邮件从这个账户中发出。邮件头对象msg
使用MIMEMultipart()
方法创建,然后添加了主题、收件人和发件人。
在当前路径中寻找.gif格式图片时用到了Python中的正则表达式处理模块。图片附件对象img
由email.mime.image
模块中的MIMEImage()
方法创建。然后为图片对象设定了正确的首部,最后再把图片附加到前面创建的msg
对象上。如这个攻略所示,我们可以在for
循环中附加多个图片。使用类似的方式,还可以添加纯文本附件。
为了发送电子邮件,我们创建了一个SMTP会话,并在这个会话对象上调用了测试方法,例如ehlo()
和starttls()
。然后使用用户名和密码登录谷歌的SMTP服务器,再调用sendmail()
方法发送电子邮件。
5.8 使用CGI为基于Python的Web服务器编写一个留言板
通用网关接口(Common Gateway Interface,简称CGI)是Web编程的一种标准。通过CGI,可以使用脚本生成服务器输出。你可能想获取用户在浏览器中输入的表单数据,重定向到另一个页面,确认收到了用户执行的操作。
5.8.1 实战演练
我们首先要运行一个支持CGI脚本的Web服务器。我们把用Python编写的CGI脚本放在cgi-bin/子目录中,然后访问包含反馈表单的HTML页面。提交表单后,Web服务器把表单数据发送给CGI脚本,我们会看到这个脚本生成的输出。
代码清单5-7展示了如何让使用Python编写的Web服务器支持CGI:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import os
import cgi
import argparse
import BaseHTTPServer
import CGIHTTPServer
import cgitb
cgitb.enable() ## enable CGI error reporting
def web_server(port):
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler #RequestsHandler
server_address = ("", port)
handler.cgi_directories = ["/cgi-bin", ]
httpd = server(server_address, handler)
print "Starting web server with CGI support on port: %s ..." %port
httpd.serve_forever()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CGI Server Example')
parser.add_argument('--port', action="store", dest="port", type=int, required=True)
given_args = parser.parse_args()
web_server(given_args.port)
下面的截图展示了启用CGI的Web服务器正在伺服内容:
运行这个脚本后,会看到如下输出:
$ python 5_7_cgi_server.py --port=8800
Starting web server with CGI support on port: 8800 ...
localhost - - [19/May/2013 18:40:22] "GET HTTP1.1" 200 -
现在,你要在浏览器中访问http://localhost:8800/5_7_send_feedback.html。
你会看到一个输入表单。假设你在表单中填写了下面的内容:
Name: User1
Comment: Comment1
下面的截图展示了在网页表单中输入用户评论的过程:
然后浏览器会被重定向到http://localhost:8800cgi-bin5_7getfeedback.py,在这个页面中你会看到如下输出:
User1 sends a comment: Comment1
用户的评论会显示在浏览器中:
5.8.2 原理分析
我们创建了一个简单的HTTP服务器,以支持处理CGI请求。Python在模块BaseHTTPServer
和CGIHTTPserver
中提供了这些接口。
我们配置了处理程序,使用路径/cgi-bin存放CGI脚本。其他路径不能运行CGI脚本。
文件5_7_send_feedback.html中的HTML反馈表单显示了一个很简单的HTML表单,包含以下代码:
<html>
<body>
<form action="cgi-bin5_7getfeedback.py" method="post">
Name: <input type="text" name="Name"> <br >
Comment: <input type="text" name="Comment" >
<input type="submit" value="Submit" >
<form>
</body>
</html>
注意,这个表单的方法是POST
,action
属性被设为了cgi-bin5_7getfeedback.py文件。这个文件的内容如下:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 5
# This program requires Python 2.7 or any later version
# Import modules for CGI handling
import cgi
import cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
name = form.getvalue('Name')
comment = form.getvalue('Comment')
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>CGI Program Example </title>"
print "</head>"
print "<body>"
print "<h2> %s sends a comment: %s</h2>" % (name, comment)
print "</body>"
print "</html>"
在这个CGI脚本中,调用cgilib
模块中的FieldStorage()
方法,得到一个用于处理HTML表单输入的表单对象。然后使用getvalue()
方法处理两个输入(name
和comment
)。最后,这个脚本显示一行文字,提示某个用户发送了一篇评论,作为用户输入的反馈。