本文用一個“網上書店”的web應用示例,簡要介紹如何用Python實現基於CGI標准的Web應用,介紹python的cgi模塊、cigtb模塊對編寫CGI腳本提供的支持。
CGI簡介
CGI Common Gateway Interface (通用網關接口),是一個Internet標准,允許Web服務器運行一個服務器端程序,稱為CGI腳本。一般的,CGI腳本都放在一個名為cgi-bin的特殊文件夾內,這樣web服務器就知道到哪里查找cgi腳本。
CGI Architecture Diagram
When a request arrives, HTTP server execute 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應用目錄結構
(操作系統:win7;python版本:3.3)
BookWebApp|
|cgi-bin
------|book_detail_view.py
------|book_list_view.py
------|template
----|yate.py
------|mode
----|Book.by
------|service
----|book_service.py
|resource
------- |books.png
|book.txt
|index.html
|run_server.py
1、Web服務器
所有的Web應用都要在Web服務器上運行,實際上所有的web服務器都支持CGI,無論是Apache、IIS、nginx、Lighttpd還是其他服務器,它們都支持用python編寫的cgi腳本。這些web服務器都比較強大,這里我們使用python自帶的簡單的web服務器,這個web服務器包含在http.server庫模塊中。
run_server.py:
運行此程序,即啟動此web應用。
from http.server import HTTPServer, CGIHTTPRequestHandler port = 8081 httpd = HTTPServer(('', port), CGIHTTPRequestHandler) print("Starting simple_httpd on port: " + str(httpd.server_port)) httpd.serve_forever()
2、index.html
首頁;URL: “http://localhost:8081/cgi-bin/book_list_view.py” 將調用 cgi-bin文件夾下的book_list_view.py
<html> <head> <title>BookStore</title> </head> <body> <h1>Welcome to My Book Store.</h1> <img src="resource/books.png"> <h3> please choose your favorite book, click <a href="cgi-bin/book_list_view.py">here</a>. </h3> <p> <strong> Enjoy!</strong> </p> </body> </html>
3、book_list_view.py
圖書清單頁面。用戶選擇要查看的圖書,提交表單,然后調動圖書詳細界面。
#Python標准庫中定義的CGI跟蹤模塊:cgibt import cgitb cgitb.enable() #啟用這個模塊時,會在web瀏覽器上顯示詳細的錯誤信息。enable()函數打開CGI跟蹤 #CGI腳本產生一個異常時,Python會將消息顯示在stderr(標准輸出)上。CGI機制會忽略這個輸出,因為它想要的只是CGI的標准輸出(stdout) import template.yate as yate import service.book_service as book_service #CGI標准指出,服務器端程序(CGI腳本)生成的任何輸出都將會由Web服務器捕獲,並發送到等待的web瀏覽器。具體來說,會捕獲發送到Stdout(標准輸出)的所有內容
#一個CGI腳本由2部分組成, 第一部分輸出 Response Headers, 第二部分輸出常規的html.
print("Content-type:text/html\n")#Response Headers #網頁內容:有html標簽組成的文本 print('<html>') print('<head>') print('<title>Book List</title>') print('</head>') print('<body>') print('<h2>Book List:</h2>') print(yate.start_form('book_detail_view.py')) book_dict=book_service.get_book_dict() for book_name in book_dict: print(yate.radio_button('bookname',book_dict[book_name].name)) print(yate.end_form('detail')) print(yate.link("/index.html",'Home')) print('</body>') print('</html>')
4、yate.py
自定義的簡單模板,用於快捷生成html
def start_form(the_url, form_type="POST"): return('<form action="' + the_url + '" method="' + form_type + '">') def end_form(submit_msg="Submit"): return('<input type=submit value="' + submit_msg + '"></form>') def radio_button(rb_name, rb_value): return('<input type="radio" name="' + rb_name + '" value="' + rb_value + '"> ' + rb_value + '<br />') def u_list(items): u_string = '<ul>' for item in items: u_string += '<li>' + item + '</li>' u_string += '</ul>' return(u_string) def header(header_text, header_level=2): return('<h' + str(header_level) + '>' + header_text + '</h' + str(header_level) + '>') def para(para_text): return('<p>' + para_text + '</p>') def link(the_link,value): link_string = '<a href="' + the_link + '">' + value + '</a>' return(link_string)
5、book_detail_view.py
圖書詳細頁面
import cgitb cgitb.enable() import cgi import template.yate as yate import service.book_service as book_service import template.yate as yate #使用cig.FieldStorage() 訪問web請求發送給web服務器的數據,這些數據為一個Python字典 form_data = cgi.FieldStorage() print("Content-type:text/html\n") print('<html>') print('<head>') print('<title>Book List</title>') print('</head>') print('<body>') print(yate.header('Book Detail:')) try: book_name = form_data['bookname'].value book_dict=book_service.get_book_dict() book=book_dict[book_name] print(book.get_html) except KeyError as kerr: print(yate.para('please choose a book...')) print(yate.link("/index.html",'Home')) print(yate.link("/cgi-bin/book_list_view.py",'Book List')) print('</body>') print('</html>')
6、Book.py
圖書類
from template import yate class Book: def __init__(self,name,author,price): self.name=name self.author=author self.price=price @property def get_html(self): html_str='' html_str+=yate.header('BookName:',4)+yate.para(self.name) html_str+=yate.header('Author:',4)+yate.para(self.author) html_str+=yate.header('Price:',4)+yate.para(self.price) return(html_str)
7、book_service.py
圖書業務邏輯類
from model.Book import Book def get_book_dict(): book_dict={} try: with open('book.txt','r') as book_file: for each_line in book_file: book=parse(each_line) book_dict[book.name]=book except IOError as ioerr: print("IOErr:",ioerr) return(book_dict) def parse(book_info): (name,author,price)=book_info.split(';') book=Book(name,author,price) return(book)
8、book.txt
待顯示的圖書信息(書名;作者;價格)
The Linux Programming Interface: A Linux and UNIX System Prog;Michael Kerrisk;$123.01
HTML5 and CSS3, Illustrated Complete (Illustrated Series);Jonathan Meersman Sasha Vodnik;$32.23
Understanding the Linux Kernel;Daniel P. Bovet Marco Cesati;$45.88
Getting Real;Jason Fried, Heinemeier David Hansson, Matthew Linderman;$87.99
測試結果
運行run_server.py,瀏覽器訪問:
http://localhost:8081/

控制台會監控請求的信息:

點擊“here”,查看圖書清單,即書名列表

選擇書名,點擊“detail”提交表單,返回該書的詳細信息:書名、作者、價格

(轉載請注明出處 ^.^)