Python CGI プログラミング


CGIとは

CGIは現在NCSAによって保守されており、NCSAはCGIを次のように定義しています:

CGI(Common Gateway Interface)、共通ゲートウェイインターフェースは、サーバー上で動作するプログラムであり、例:HTTPサーバー上で、クライアントのHTMLページへのインターフェースを提供します。

注意:

Python 3.11以降、cgiモジュールは公式に「非推奨」(Deprecated)とマークされ、Python 3.13から削除されました。

移行を推奨Flask、Django 或 FastAPIなどの最新のWebフレームワーク。


ウェブページの閲覧

CGIがどのように動作するかをよりよく理解するために、ウェブページ上でリンクやURLをクリックしたときの流れを見てみましょう:

  • 1. ブラウザを使用してURLにアクセスし、HTTP Webサーバーに接続します。
  • 2. Webサーバーはリクエスト情報を受信するとURLを解析し、アクセスされたファイルがサーバー上に存在するかどうかを確認します。存在する場合はファイルの内容を返し、存在しない場合はエラー情報を返します。
  • 3. ブラウザはサーバーから情報を受信し、受信したファイルまたはエラー情報を表示します。

CGIプログラムは、Pythonスクリプト、PERLスクリプト、SHELLスクリプト、CまたはC++プログラムなどです。


CGIアーキテクチャ図

cgiarch


Webサーバーのサポートと設定

CGIプログラミングを行う前に、WebサーバーがCGIをサポートし、CGIのハンドラが設定されていることを確認してください。

ApacheのCGI設定サポート:

CGIディレクトリを設定します:

ScriptAlias /cgi-bin/ /var/www/cgi-bin/

すべてのHTTPサーバーで実行されるCGIプログラムは、事前に設定されたディレクトリに保存されます。このディレクトリはCGIディレクトリと呼ばれ、慣例として /var/www/cgi-bin ディレクトリと命名されています。

CGIファイルの拡張子は.cgi、Pythonでは.py拡張子を使用できます。

デフォルトでは、Linuxサーバーは /var/www のcgi-binディレクトリで実行するように設定されています。

CGIスクリプトを実行する別のディレクトリを指定したい場合は、httpd.conf 設定ファイルを次のように変更できます:

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

AddHandler に .py 接尾辞を追加すると、.py で終わるPythonスクリプトファイルにアクセスできるようになります:

AddHandler cgi-script .cgi .pl .py

最初のCGIプログラム

Pythonを使用して最初のCGIプログラムを作成します。ファイル名は hello.py で、ファイルは /var/www/cgi-bin ディレクトリにあり、内容は次のとおりです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

print "Content-type:text/html"
print                               # 空行,告诉服务器结束头部
print '<html>'
print '<head>'
print '<meta charset="utf-8">'
print '<title>Hello World - 我的第一个 CGI 程序!</title>'
print '</head>'
print '<body>'
print '<h2>Hello World! 我是来自サンプル的第一CGI程序</h2>'
print '</body>'
print '</html>'

ファイルを保存した後、hello.py のファイル権限を755に変更します:

chmod 755 hello.py 

上記のプログラムをブラウザでアクセスするとhttp://localhost/cgi-bin/hello.py表示結果は次のとおりです:

Hello World! 我是来自サンプル的第一CGI程序

このhello.pyスクリプトは単純なPythonスクリプトです。スクリプトの最初の行の出力内容「Content-type:text/html」はブラウザに送信され、ブラウザに表示するコンテンツタイプが「text/html」であることを通知します。

print を使用して空行を出力し、サーバーにヘッダー情報の終了を知らせます。


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の設定

CGI環境変数

すべてのCGIプログラムは以下の環境変数を受け取ります。これらの変数はCGIプログラムで重要な役割を果たします:

変数名説明
CONTENT_TYPEこの環境変数の値は、渡された情報のMIMEタイプを示します。現在、環境変数CONTENT_TYPEは通常 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_FILENAMECGIスクリプトの完全なパス
SCRIPT_NAMECGIスクリプトの名前
SERVER_NAMEこれはWebサーバーのホスト名、別名、またはIPアドレスです。
SERVER_SOFTWAREこの環境変数の値には、CGIプログラムを呼び出したHTTPサーバーの名前とバージョン番号が含まれます。例えば、上記の値は Apache/2.2.14(Unix) です。

以下は、CGI環境変数を出力する簡単なCGIスクリプトです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# filename:test.py

import os

print "Content-type: text/html"
print
print "<meta charset=\"utf-8\">"
print "<b>环境变量</b><br>";
print "<ul>"
for key in os.environ.keys():
    print "<li><span style='color:green'>%30s </span> : %s </li>" % (key,os.environ[key])
print "</ul>"

上記のコードを test.py として保存し、ファイル権限を755に変更して実行した結果は次のとおりです:


GETおよびPOSTメソッド

ブラウザクライアントは2つの方法でサーバーに情報を渡します。これらの2つの方法は、GETメソッドとPOSTメソッドです。

GETメソッドを使用したデータ転送

GETメソッドはエンコードされたユーザー情報をサーバーに送信します。データ情報はリクエストページのURLに含まれ、「?」記号で区切られます。次のようになります:

http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
GETリクエストに関するその他の注意事項:
  • GETリクエストはキャッシュされる可能性があります
  • GETリクエストはブラウザの履歴に残ります
  • GETリクエストはブックマークに保存できます
  • GETリクエストは機密データを処理するときに使用すべきではありません
  • GETリクエストには長さの制限があります
  • GETリクエストはデータの取得にのみ使用すべきです

シンプルなURL例:GETメソッド

以下は、GETメソッドを使用してhello_get.pyプログラムに2つのパラメータを送信する簡単なURLです:

/cgi-bin/test.py?name=サンプル&url=http://www.example.com

以下はhello_get.pyファイルのコードです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# filename:test.py

# CGI处理模块
import cgi, cgitb 

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

# 获取数据
site_name = form.getvalue('name')
site_url  = form.getvalue('url')

print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>サンプル CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2>%s官网:%s</h2>" % (site_name, site_url)
print "</body>"
print "</html>"

ファイルを保存した後、hello_get.py のファイル権限を755に変更します:

chmod 755 hello_get.py 

ブラウザでリクエストした出力結果:

シンプルなフォーム例:GETメソッド

以下は、HTMLフォームを使用してGETメソッドでサーバーに2つのデータを送信する例です。送信先のサーバースクリプトは同じくhello_get.pyファイルです。hello_get.html のコードは次のとおりです:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
<form action="../cgi-bin/hello_get.py" method="get">
站点名称: <input type="text" name="name">  <br />

站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>

デフォルトでは、cgi-bin ディレクトリにはスクリプトファイルのみを保存できます。hello_get.html を test ディレクトリに保存し、ファイルの権限を 755 に変更します:

chmod 755 hello_get.html

Gif デモは次のようになります:

POSTメソッドを使用したデータ送信

POST メソッドを使用してサーバーにデータを送信することは、より安全で信頼性があります。ユーザーパスワードなどの機密情報は POST を使用してデータを転送する必要があります。

以下も同じ hello_get.py ですが、ブラウザが送信した POST フォームデータも処理できます:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# CGI处理模块
import cgi, cgitb 

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

# 获取数据
site_name = form.getvalue('name')
site_url  = form.getvalue('url')

print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>サンプル CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2>%s官网:%s</h2>" % (site_name, site_url)
print "</body>"
print "</html>"

以下はフォームが POST メソッド(method="post")を使用してサーバースクリプト hello_get.py にデータを送信する例です:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
<form action="../cgi-bin/hello_get.py" method="post">
站点名称: <input type="text" name="name">  <br />

站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>

Gif デモは次のようになります:

CGIプログラムを介したチェックボックスデータの送信

checkbox は1つまたは複数のオプションデータを送信するために使用されます。HTML コードは次のとおりです:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
<form action="../cgi-bin/checkbox.py" method="POST" target="_blank">
<input type="checkbox" name="サンプル" value="on" /> サンプル
<input type="checkbox" name="google" value="on" /> Google
<input type="submit" value="选择站点" />
</form>
</body>
</html>

以下は checkbox.py ファイルのコードです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

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

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

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

if form.getvalue('サンプル'):
   サンプル_flag = "是"
else:
   サンプル_flag = "否"

print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>サンプル CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> サンプル是否选择了 : %s</h2>" % サンプル_flag
print "<h2> Google 是否选择了 : %s</h2>" % google_flag
print "</body>"
print "</html>"

checkbox.py の権限を変更します:

chmod 755 checkbox.py

ブラウザでアクセスする Gif デモ図:

CGIプログラムを介したラジオデータの送信

Radio はサーバーに1つのデータのみを送信します。HTML コードは次のとおりです:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
<form action="../cgi-bin/radiobutton.py" method="post" target="_blank">
<input type="radio" name="site" value="サンプル" /> サンプル
<input type="radio" name="site" value="google" /> Google
<input type="submit" value="提交" />
</form>
</body>
</html>

radiobutton.py スクリプトコードは次のとおりです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 引入 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 "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>サンプル CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 选中的网站是 %s</h2>" % site
print "</body>"
print "</html>"

radiobutton.py の権限を変更します:

chmod 755 radiobutton.py

ブラウザでアクセスする Gif デモ図:

CGIプログラムを介したテキストエリアデータの送信

Textarea はサーバーに複数行のデータを送信します。HTML コードは次のとおりです:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
<form action="../cgi-bin/textarea.py" method="post" target="_blank">
<textarea name="textcontent" cols="40" rows="4">
在这里输入内容...
</textarea>
<input type="submit" value="提交" />
</form>
</body>
</html>

textarea.py スクリプトコードは次のとおりです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 引入 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"
print
print "<html>"
print "<head>";
print "<meta charset=\"utf-8\">"
print "<title>サンプル CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 输入的内容是:%s</h2>" % text_content
print "</body>"
print "</html>"

textarea.py の権限を変更します:

chmod 755 textarea.py

ブラウザでアクセスする Gif デモ図:

CGIプログラムを介したドロップダウンデータの送信。

HTML ドロップダウンボックスのコードは次のとおりです:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
<form action="../cgi-bin/dropdown.py" method="post" target="_blank">
<select name="dropdown">
<option value="サンプル" selected>サンプル</option>
<option value="google">Google</option>
</select>
<input type="submit" value="提交"/>
</form>
</body>
</html>

dropdown.py スクリプトコードは次のとおりです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

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

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

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

print "Content-type:text/html"
print
print "<html>"
print "<head>"
print "<meta charset=\"utf-8\">"
print "<title>サンプル CGI 测试实例</title>"
print "</head>"
print "<body>"
print "<h2> 选中的选项是:%s</h2>" % dropdown_value
print "</body>"
print "</html>"

dropdown.py の権限を変更します:

chmod 755 dropdown.py

ブラウザでアクセスする Gif デモ図:


CGIでのCookieの使用

http プロトコルの大きな欠点は、ユーザーの身分を判断しないことです。これによりプログラマーに大きな不便をもたらしますが、cookie 機能の登場によってこの不足が補われました。

cookie は、クライアントがスクリプトにアクセスする際に、クライアントのブラウザを介してクライアントのハードディスクに記録データを書き込み、次回クライアントがスクリプトにアクセスしたときにデータ情報を取得することで、身分を判別する機能を実現します。cookie は身分検証でよく使用されます。

 

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.example.com"
  • secure:このフラグが指定された場合、cookie は SSL プロトコルの https サーバーを介してのみ転送できることを示します。
  • cookie の受信は環境変数 HTTP_COOKIE を設定することで実現されます。CGI プログラムはこの変数を検索して cookie 情報を取得できます。

Cookieの設定

Cookie の設定は非常に簡単です。cookie は http ヘッダーで個別に送信されます。以下の例では、cookie に name と expires を設定しています:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 
print 'Content-Type: text/html'
print 'Set-Cookie: name="サンプル";expires=Wed, 28 Aug 2016 18:30:00 GMT'
print
print """
<html>
    <head>
        <meta charset="utf-8">
        <title>サンプル(example.com)</title>
    </head>
    <body>
        <h1>Cookie set OK!</h1>
    </body>
</html>
"""

上記のコードを cookie_set.py に保存し、cookie_set.py の権限を変更します:

chmod 755 cookie_set.py

上記の例では Set-Cookie ヘッダー情報を使用して Cookie 情報を設定しています。オプションでは、有効期限 Expires、ドメイン名 Domain、パス Path など Cookie の他の属性を設定しています。これらの情報は "Content-type:text/html" の前に設定されます。


Cookie情報の取得

Cookie 情報の取得ページは非常に簡単です。Cookie 情報は CGI の環境変数 HTTP_COOKIE に格納され、格納形式は次のとおりです:

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

以下は、cookie 情報を取得する簡単な CGI プログラムです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 导入模块
import os
import Cookie

print "Content-type: text/html"
print

print """
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
<h1>读取cookie信息</h1>
"""

if 'HTTP_COOKIE' in os.environ:
    cookie_string=os.environ.get('HTTP_COOKIE')
    c=Cookie.SimpleCookie()
    c.load(cookie_string)

    try:
        data=c['name'].value
        print "cookie data: "+data+"<br>"
    except KeyError:
        print "cookie 没有设置或者已过期<br>"
print """
</body>
</html>

"""

上記のコードを cookie_get.py に保存し、cookie_get.py の権限を変更します:

chmod 755 cookie_get.py

上記の cookie 設定の色の Gif は次のとおりです:

ファイルアップロードの例

アップロードファイルを設定する HTML フォームでは、以下の設定が必要です:enctype属性をmultipart/form-dataに設定します。コードは次のとおりです:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
 <form enctype="multipart/form-data" 
                     action="../cgi-bin/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
# -*- coding: UTF-8 -*-

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>
<head>
<meta charset="utf-8">
<title>サンプル(example.com)</title>
</head>
<body>
   <p>%s</p>
</body>
</html>
""" % (message,)

上記のコードを save_file.py に保存し、save_file.py の権限を変更します:

chmod 755 save_file.py

上記の cookie 設定の色の Gif は次のとおりです:

使用しているシステムが Unix/Linux の場合、ファイル区切り文字を置き換える必要があります。Windows では open() ステートメントを使用するだけで済みます:

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

ファイルダウンロードダイアログ

まず現在のディレクトリに foo.txt ファイルを作成し、プログラムのダウンロードに使用します。

ファイルのダウンロードは HTTP ヘッダー情報の設定によって実現されます。機能コードは次のとおりです:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# HTTP 头部
print "Content-Disposition: attachment; filename=\"foo.txt\"";
print
# 打开文件
fo = open("foo.txt", "rb")

str = fo.read();
print str

# 关闭文件
fo.close()
その他の拡張