Python3之CGI

一、概述

CGI(Common Gateway Interface)也叫通用网关接口,它是一个web服务器主机提供信息服务的标准接口,只要遵循这个接口,web服务器就能获取客户端提交的信息,转交给服务端的CGI程序进行处理,然后将处理结果返回给客户端。
CGI通讯由两部分组成:一部分是用户的浏览器显示的页面,也就是html页面,另一部分是运行在服务器上的CGI程序。

二、CGI架构

CGI是Web服务器和一个独立的进程之间的协议,它会把HTTP请求 Request 的 Header 头设置成进程的环境变量,HTTP 请求的 Body 正文设置成进程的标准输入,进程的标准输出设置为HTTP响应 Response,包含 Header 头和 Body 正文。
Python3之CGI_第1张图片

三、Web服务器环境搭建(windows)

本例使用Apache2.4.x + Python3.x + windows搭建Web服务器实现CGI编程

3.1、 安装Apache2.4.x

官方下载地址:Apache2.4.x下载地址
Python3之CGI_第2张图片
需要:VC14 需要下载Microsoft Visual C++ 2015,官方地址为:Microsoft Visual C++ 2015
Python3之CGI_第3张图片

3.2、 Apache2.4.x配置(配置路径:Apache24–>conf–>httpd.conf)

A、配置Apache文件的根目录:设置SRVROOT根目录为电脑端Apache的存放路径。为方便后期修改方便,可利用引用“${SRVROOT}”的方式来书写配置,即:
Python3之CGI_第4张图片
注:Apache2.2.x版本修改“ServerRoot”

B、修改端口配置:默认端口80,此处改为1024
Python3之CGI_第5张图片
C、设置python文档的存储路径,以便Python解释器能够找到它们(即设置访问权限),此处路径设置为“D:\Apache24\cgi-bin”,找到如下内容:
Python3之CGI_第6张图片
修改为:
Python3之CGI_第7张图片
配置文件:

<Directory "${SRVROOT}/cgi-bin">
   AllowOverride None
   Options Indexes FollowSymLinks ExecCGI
   Require all granted
   Require host ip
</Directory>

注意:httpd.conf里面还有一个标签,此处不用改
Python3之CGI_第8张图片
D、添加.py 后缀:在 AddHandler 中添加 .py 后缀,这样就可以访问 .py 结尾的 python 脚本文件
Python3之CGI_第9张图片
E、开启cgi_module modules/mod_cgi.so模块(即去掉模块前的注释#):
Python3之CGI_第10张图片
F:启动apach服务器(Apache24\bin目录下)
Apache24\bin目录下,使用“httpd -k install -n apache”安装Apache主服务,然后输入“httpd –k start”开启Apache服务(管理员身份运行DOS)
Python3之CGI_第11张图片
G:编写.py脚本

#!D:/Python/Python37/python.exe
# -*- coding: utf-8 -*-
print ("Content-type:text/html\n\n")
print                               # 空行,告诉服务器结束头部
print ('')
print ('')
print ('')
print ('this ia a CGI program !!!')
print ('')
print ('')
print ('

this ia a CGI program !!!

'
) print ('') print ('')

脚本说明:
1、第一行是申明电脑端解释器的位置,注意一定要写第一行,这是python的格式规范

2、因电脑端安装的是Python3.7,输出语句print("")一定要加括号,一定要注意,不然之后会报500错误

3、第三行的print (“Content-type:text/html\n\n”),这是声明格式,此处注意,其中的“\n\n”,两个“\n”,一个都不能少,少了也会报500错误
4、ScriptAlias /cgi-bin/ "${SRVROOT}/cgi-bin/"设置了解释器查找.py文件的方法
在这里插入图片描述
H:浏览器输入“http://localhost:1024/cgi-bin/hello.py”即可运行hello.py文件
Python3之CGI_第12张图片
I:修改cgi-bin中.py文件路径为任意位置:修改httpd.conf中和ScriptAlias下路径Python3之CGI_第13张图片
Python3之CGI_第14张图片
J:修改Apache中.html文件存放路径:修改httpd.conf中DocumentRoot和的配置,即:
在这里插入图片描述

四、CGI编程解析

4.1、 HTTP头部

上文中hello.py文件中的" Content-type:text/html"为HTTP头部的一部分,它会发送给浏览器告诉浏览器文件的内容类型。
HTTP头部格式:HTTP 字段名: 字段内容
如:Content-type:text/html
CGI程序中HTTP头部经常使用的信息如下:

头部信息 描述
Content-type: 请求的与实体对应的MIME信息,例如: Content-type:text/html
Expires: Date 响应过期的日期和时间
Location: URL 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源
Last-modified: Date 请求资源的最后修改时间
Content-length: N 请求的内容长度
Set-Cookie: String 设置Http Cookie

4.2、 CGI环境变量

所有的CGI程序都会接收以下环境变量,这些环境变量在CGI程序中发挥了重要的作用,具体如下:

环境变量名 描述
CONTENT_TYPE CONTENT_TYPE的值指示所传递来的信息的MIME类型。一般情况下该变量的值是:application/x-www-form-urlencoded,表示数据来自于HTML表单
CONTENT_LENGTH 假如服务器与CGI程序信息的传递方式是POST,这个环境变量即从标准输入STDIN中可以读到的有效数据的字节数。这个环境变量在读取所输入的数据时必须使用。
HTTP_COOKIE 客户机内的 COOKIE 内容
HTTP_USER_AGENT 提供包含了版本数或其他专有数据的客户浏览器信息
PATH_INFO 这个环境变量的值表示紧接在CGI程序名之后的其他路径信息。它常常作为CGI程序的参数出现。
QUERY_STRING 如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值即所传递的信息。这个信息经跟在CGI程序名的后面,两者中间用一个问号’?'分隔。
REMOTE_ADDR 这个环境变量的值是发送请求的客户机的IP地址,例如上面的192.168.1.67。这个值总是存在的。而且它是Web客户机需要提供给Web服务器的唯一标识,可以在CGI程序中用它来区分不同的Web客户机。
REMOTE_HOST 这个环境变量的值包含发送CGI请求的客户机的主机名。如果不支持你想查询,则无需定义此环境变量。
REQUEST_METHOD 提供脚本被调用的方法。对于使用 HTTP/1.0 协议的脚本,仅 GET 和 POST 有意义。
SCRIPT_FILENAME CGI脚本的完整路径
SCRIPT_NAME CGI脚本的的名称
SERVER_NAME 这是你的 WEB 服务器的主机名、别名或IP地址。
SERVER_SOFTWARE 这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(Unix)
#!D:/Python/Python37/python.exe
import os
# -*- coding: utf-8 -*-
print ("Content-type:text/html\n\n")
print                               # 空行,告诉服务器结束头部
print ('')
print ('')
print ('')
print ('this ia CGI environment variable')
print ('')
print ('')
print ('

this ia CGI environment variable

'
) print ("
    ") for key in os.environ.keys(): print ("
  • %30s : %s
  • "
    % (key,os.environ[key])) print ("
"
) print ('') print ('')

浏览器输入“http://localhost:9999/cgi-bin/ZX_02_Environment_Variable.py”,结果如下:
Python3之CGI_第15张图片

4.3、 CGI使用GET方法传输数据

GET方法发送编码后的用户信息到服务端,数据信息包含在请求页面的URL上,以"?"号分割
GET请求需注意如下内容:
1、GET 请求可被缓存
2、GET 请求保留在浏览器历史记录中
3、GET 请求可被收藏为书签
4、GET 请求不应在处理敏感数据时使用
5、GET 请求有长度限制
6、GET 请求只应当用于取回数据

A、GET方法处理URL链接

步骤:
1、导入cgi库

import cgi

2、创建 FieldStorage 的实例化

form = cgi.FieldStorage()

3、通过表单获取数据

key = form.getvalue('value')

get_url.py文件内容:

#!D:/Python/Python37/python.exe

# 导入cgi处理模块
import cgi

# 实例化FieldStorage,代表整个表单
form = cgi.FieldStorage()

# 通过表单获取数据
name = form.getvalue("name")
pwd = form.getvalue("pwd")

print("Content-type:text/html\n\n")
print
print('')
print('')
#print('') 注释掉该行后web显示可正常显示中文
print('这是网址的title')
print('')
print("")
print('

姓名:%s

'
% name) print('

密码:%s

'
% pwd) print("") print('')

浏览器输入“http://localhost:9999/cgi-bin/get_url.py?name=june&pwd=aaaaaaaa”后web页面显示
Python3之CGI_第16张图片

B、GET方法处理HTML表单

get_url.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>白杨(Gavin.ZX.com)</title>
</head>
<body>
<form action="/cgi-bin/ZX_03_GET_URL.py" method="get">
用户名: <input type="text" name="name">  <br />

密码: <input type="text" name="pwd" />
<input type="submit" value="提交" />
</form>
</body>
</html>

get_url.py文件内容:

#!D:/Python/Python37/python.exe

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

# 实例化FieldStorage,代表整个表单
form = cgi.FieldStorage()

# 通过表单获取数据
name = form.getvalue("name")
pwd = form.getvalue("pwd")

print("Content-type:text/html\n\n")
print
print('')
print('')
#print('') 注释掉该行后web显示可正常显示中文
print('这是网址的title')
print('')
print("")
print('

姓名:%s

'
% name) print('

密码:%s

'
% pwd) print("") print('')

浏览器输入“http://localhost:9999/get_url.html”,结果如下:

Python使用get方式传输html表单

C、POST方法处理HTML表单数据

post_url.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>白杨(Gavin.ZX.com)</title>
</head>
<body>
<form action="/cgi-bin/ZX_04_POST_URL.py" method="post">
用户名: <input type="text" name="name">  <br />

密码: <input type="text" name="pwd" />
<input type="submit" value="提交" />
</form>
</body>
</html>

post_url.py文件内容:

#!D:/Python/Python37/python.exe

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

# 实例化FieldStorage,代表整个表单
form = cgi.FieldStorage()

# 通过表单获取数据
name = form.getvalue("name")
pwd = form.getvalue("pwd")

print("Content-type:text/html\n\n")
print
print('')
print('')
#print('') 注释掉该行后web显示可正常显示中文
print('POST方法传输HTML数据')
print('')
print("")
print('

姓名:%s

'
% name) print('

密码:%s

'
% pwd) print("") print('')

浏览器输入“http://localhost:9999/post_url.html”,结果如下:

python使用post传输html数据

D、CGI程序传输checkbox数据

checkbox通常用于提交一个或者多个选项数据,其HTML文件如下:
CheckBox.html文件:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>利用CGI传输CheckBox数据</title>
</head>
<body>
<form action="/cgi-bin/ZX_05_CheckBox.py" method="POST" target="_blank">
<input type="checkbox" name="baidu" value="on" /> 百度
<input type="checkbox" name="google" value="on" /> 谷歌
<input type="submit" value="选择站点" />
</form>
</body>
</html>

CheckBox.py文件:

#!D:/Python/Python37/python.exe

# 引入 CGI 处理模块
import cgi, cgitb

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

# 接收字段数据
if form.getvalue('google'):
   google_flag = "是"
else:
   google_flag = "否"

if form.getvalue('baidu'):
   baidu_flag = "是"
else:
   baidu_flag = "否"

print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("利用CGI传输CheckBox数据")
print ("")
print ("")
print ("

百度被选择 : %s

"
% baidu_flag) print ("

谷歌被选择 : %s

"
% google_flag) print ("") print ("")

浏览器输入“http://localhost:9999/checkbox.html”,结果如下:

python使用CGI程序传输checkbox数据

E、CGI程序传递Radio数据

Radio 仅能向服务器传递一个数据
Radio.html文件为:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>利用CGI传输Radio数据</title>
</head>
<body>
<form action="/cgi-bin/ZX_06_Radio.py" method="POST" target="_blank">
<input type="radio" name="site" value="baidu" /> Baidu
<input type="radio" name="site" value="google" /> Google
<input type="submit" value="选择站点" />
</form>
</body>
</html>

Radio.py文件:

#!D:/Python/Python37/python.exe

# 引入 CGI 处理模块
import cgi, cgitb

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

# 接收字段数据
if form.getvalue('site'):
   site = form.getvalue('site')
else:
   site = "空"

print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("利用CGI传输Radio数据")
print ("")
print ("")
print ("

选中的网站是 %s

"
% site) print ("") print ("")

浏览器输入“http://localhost:9999/radio.html”,结果如下:

CGI程序传递Radio数据

E、CGI程序传递 Textarea 数据

Textarea 可向服务器传递多行数据
Textarea.html文件:




    
    通过CGI程序传递 Textarea数据


Textarea.py文件:

#!D:/Python/Python37/python.exe

import cgi , cgitb

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

# 接收字段数据
if form.getvalue("textcontent"):
    content = form.getvalue("textcontent")
else:
    content = "没有内容"

print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("通过CGI程序传递Textarea数据")
print ("")
print ("")
print ("

通过CGI程序传递Textarea数据是:%s

"
% content) print ("") print ("")

浏览器输入“http://localhost:9999/Textarea.html”,结果如下:

CGI程序传递Textarea数据

F、CGI程序传递下拉数据

dropdown.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CGI程序传递下拉数据</title>
</head>
<body>
<form action="/cgi-bin/ZX_08_Dropdown.py" method="post" target="_blank">
<select name="dropdown">
<option value="baidu" selected>百度</option>
<option value="google">谷歌</option>
</select>
<input type="submit" value="提交"/>
</form>
</body>
</html>

dropdown.py文件:

#!D:/Python/Python37/python.exe

import cgi , cgitb

# 创建FiledStorage对象
form = cgi.FieldStorage()

# 接受数据
if form.getvalue("dropdown"):
    content = form.getvalue("dropdown")
else:
    content = "空"

print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("CGI程序传递下拉数据")
print ("")
print ("")
print ("

选中的选项是:%s

"
% content) print ("") print ("")

浏览器输入“http://localhost:9999/dropdown.html”,结果如下:

CGI程序传递下拉数据

G、CGI程序使用Cookie

HTTP 协议一个最大的缺点是不对用户身份进行校验,cookie 功能的出现一定程度上弥补了这个不足。

cookie 是在客户访问脚本的同时,通过客户的浏览器,在客户硬盘上写入纪录数据 ,当下次客户访问脚本时取回数据信息,从而达到身份判别的功能。

cookie 常用在身份校验中。

1、cookie语法

http 协议 cookie 的发送是通过 http 头部来实现的,其早于文件的传递,头部 set-cookie 的语法如下:

Set-cookie:name=name;expires=date;path=path;domain=domain;secure 

参数说明:
name=name:需要设置cookie的值(name不能使用";“和”,"号),多个name值时用 “;” 分隔,例如:name1=name1;name2=name2;name3=name3。

expires=date:cookie的有效期限,格式: expires=“Wdy,DD-Mon-YYYY HH:MM:SS” 。

path=path:设置cookie支持的路径,如果path是一个路径,则cookie对这个目录下的所有文件及子目录生效,例如:path="/cgi-bin/";如果path是一个文件,则cookie指对这个文件生效,例如:path="/cgi-bin/cookie.cgi"。

domain=domain:对cookie生效的域名,例如:domain=“www.runoob.com”

secure:如果给出此标志,表示cookie只能通过SSL协议的https服务器来传递。

cookie的接收是通过设置环境变量HTTP_COOKIE来实现的,CGI程序可以通过检索该变量获取cookie信息。

2、cookie设置

Cookie的设置较为简单,cookie通常会在http头部单独发送
cookie_set.py文件:

#!D:/Python/Python37/python.exe

print ('Content-Type: text/html')
print ('Set-Cookie: name="cookie";expires=Wed, 08 July 2020 15:30:00 GMT')
print ()
print ("""

  
    
    cookie设置
  
    
        

Cookie set OK ~~~

"""
)

浏览器输入“http://localhost:9999/cgi-bin/ZX_09_Cookie_set.py”,结果如下:

Cookie设置

3、Cookie信息检索

Cookie信息存储在CGI的环境变量HTTP_COOKIE中,存储格式如下:

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

cookie_get.py文件:

#!D:/Python/Python37/python.exe

import os , http.cookies

print("Content-type: text/html")

print()

print("""



cookie检索


cookie检索

"""
) if 'HTTP_COOKIE' in os.environ: # 获取cookie字符串 cookie_string = os.environ.get('HTTP_COOKIE') # 创建Cookie.SimpleCookie对象 cookie = http.cookies.SimpleCookie() cookie.load(cookie_string) try: data=cookie["name"].value print('

cookie data: %s

'
% data) except KeyError: print("

cookie 没有设置或者已过期

"
) print(""" """)

浏览器输入“http://localhost:9999/cgi-bin/ZX_10_Cookie_get.py”,结果如下:

Cookie信息读取

H、CGI程序长传文件

HTML设置上传文件的表单需要设置 enctype 属性为 multipart/form-data
Upload.py文件:

#!D:/Python/Python37/python.exe

import cgi, os

form = cgi.FieldStorage()

# 获取文件名,返回FieldStorage类型列表,形如:FieldStorage('filename', 'test.txt', b'Test')
field_item = form['filename']

# 检测文件是否上传,field_item.filename返回文件名称
if field_item.filename:
    # 设置文件路径,os.path.basename()返回path最后的文件名
    filename = os.path.basename(field_item.filename)
    open('E:/Mine/Python/Runoob/ZX_24_CGI/Upload/' + filename , 'wb').write(field_item.file.read())
    message = "文件" + filename + "上传成功"
else:
    message = '文件没有上传'

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



CGI文件上传


   

%s

%s

%s

"""
% (message,field_item,field_item.filename,))

Upload.html文件:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CGI文件上传</title>
</head>
<body>
 <form enctype="multipart/form-data"
                     action="/cgi-bin/ZX_11_UploadFile.py" method="post">
   <p>选中文件: <input type="file" name="filename" /></p>
   <p><input type="submit" value="上传" /></p>
   </form>
</body>
</html>

浏览器输入“http://localhost:9999/Upload.html”,结果如下:

CGI上传文件

I、CGI文件下载对话框

文件下载通过设置HTTP头信息来实现
参考文档:CGI实现文件上传和下载

DownloadFile.html文件

<!DOCTYPE html>
<html>

<head>
    <meta charset="gbk">
    <title>CGI上传和下载文件</title>
</head>

<body>
<form enctype="multipart/form-data"
    action="/cgi-bin/ZX_12_DownloadFile.py" method="post">
    <p><input type="button" value="下载" onclick="location.href='/cgi-bin/ZX_12_DownloadFile.py'" method="get"></p>
</form>
</body>

</html>

DownloadFile.py文件

#!D:/Python/Python37/python.exe

import os , sys

#下载文件

# 文件路径 Download.txt 赋值给file_path
Downloadfile_path = 'E:/Mine/Python/Runoob/ZX_24_CGI/Upload/pytest.pdf'

# 文件名赋值给 file_name
file_name=os.path.basename(Downloadfile_path)

# 获取文件大小,并赋值给file_size
file_size = os.path.getsize(Downloadfile_path)

# HTTP 头部,设置下载文件名称
print ('Content-Disposition: attachment; filename = 下载.pdf')

# 这个是文件下载框显示的文件大小
print('Accept-Length:%s'%(file_size))

#输出1行空行,告诉浏览器HTTP头部结束
print ()

# 以读取二进制的方式打开一个文件,返回的文件描述符,赋值给fd
file = open(Downloadfile_path, "rb")

#通过文件描述符 file 读取打开文件的数据,并赋值给 read_bytes
read_bytes = file.read()

#刷新标准输出流的缓冲区,如果不刷新的话,客户端就接收不到数据
sys.stdout.flush()

#把读取到的数据写入到标准输出流的缓冲区
sys.stdout.buffer.write (read_bytes)

#关闭文件描述符fd
file.close()

浏览器输入“http://localhost:9999/DownloadFile.py”,结果如下:

CGI下载文件

你可能感兴趣的:(Python)