

PythonのCGIで値の受け渡し（ページ遷移）を行っています。  
現在は `cgi.FieldStorage()` で値を取得しています。  
ですが、Python 3.11からcgiが非推奨で、3.13からは完全に廃止されるとありました。

Python 3.11で `import cgi` すると以下のエラーが出ます。

[code]

    DeprecationWarning: 'cgi' is deprecated and slated for removal in Python 3.13
    
[/code]

廃止される前に別のやり方をと思い、requestsを試してみたのですが、どのように受け取ったら良いのか分からない状態です。何か良い方法はあるのでしょうか？  
よろしくお願いします。

**環境**  
Windows Server 2012  
Python 3.10.5  
Apache 2.4.41

現在のやり方は以下です。  
test.htmlでsubmitして、test.pyで値を受け取っています。

test.html

[code]

    <html>
    <body>
    <form action="./test.py" method="post">
        <input type="text" name="text_name" value="ABC">
        <input type="submit" name="submit_name" value="送信">
    </form>
    </body>
    </html>
    
[/code]

test.py

[code]

    import cgi
    
    form = cgi.FieldStorage()
    text = form.getfirst('text_name')
    print(text)
    
[/code]


CC BY-SA 4.0
2022-11-22T07:29:58.290
92372
2023-03-16T12:04:05.507
2022-11-22T12:53:33.243
3060
54548
question
python python3 cgi
Pythonでcgi.FieldStorage() を使わずにフォームの値を受け取る方法はありますか？


Python はそれほど詳しくないので少し調べてみた程度の情報となります。

* * *

cgi モジュール廃止に関する文書に、関連する記述がありました。  
cgi を完全に置き換えるようなモジュールは無いようです。

<https://peps.python.org/pep-0594/#deprecated-modules>

利用したい機能に応じて、今回の例であれば multipart や urllib.parse.parse_qsl を使えばよい、といった記載があります。

<https://peps.python.org/pep-0594/#cgi>

質問に関連する部分のみ抜粋:

> Replacements for the various parts of cgi which are not directly related to
> executing code are:
>
>   * `FieldStorage`/`MiniFieldStorage` has no direct replacement, but can
> typically be replaced by using
> [multipart](https://pypi.org/project/multipart/) (for POST and PUT requests)
> or urllib.parse.parse_qsl (for GET and HEAD requests)
>

もしくは 既存の cgi をフォークした [legacy-cgi](https://pypi.org/project/legacy-cgi/)
というモジュールもあるようです。既存 CGI スクリプトをサポート継続するのが目的のようですが、"WSGI への移行も検討するように" とも書かれています。


CC BY-SA 4.0
2022-11-22T07:57:58.500
92373
2022-11-22T12:13:34.527
2022-11-22T12:13:34.527
3060
3060
92372
answer


データの受け取りが出来ました。  
<https://code-notes.com/lesson/20#i1>  
を参考にさせていただきました。

[code]

    import sys
    sys.stdin.read()
    
[/code]

で、POSTされたデータを受け取ることが出来たので、  
辞書にキーと値を入れることで、  
値 = 辞書.get('キー')  
と、現状の仕組みをそれほど変えることなく、ファイルデータやデータの受け取りが可能となりました。


CC BY-SA 4.0
2022-11-28T06:18:49.017
92472
2022-11-28T06:18:49.017
54548
92372
answer


### 問題があるかも知れないこと

(自己解決してるのかもだけど 注意が必要です)

> sys.stdin.read()  
>  で、POSTされたデータを受け取ることが出来たので、

CGI は標準入出力を行えるプログラミング言語であれば (Python含めたいてい)使えます  
`cgi` モジュールは

  * 各種のパース, エスケープ 逆エスケープ, エンコード・デコードなど, 用意されてるか自動的に呼び出すかしてくれるもの
  * セキュリティー上の懸念を(少しは)緩和してる(かも知れない)

基本的に, CGIでは 呼び出されたスクリプトが責任をもって処理を行ない結果を返す  
バッファオーバーフローなど引き起こすような要求に対処するのも, (たぶん)スクリプト側の責任の可能性がある (・・・ ので, (使わないよりも) `cgi`
モジュール使うほうがよいはず)

`sys.stdin` を直接利用するのではなく, `cgi`モジュールを持ってきて `mycgi` とでも変更し利用するのが まだマシなのでは？
(確証はないけれど, 予想として)  
my-cgi 的なモジュール使う場合は 現状維持であり, `sys.stdin` を利用するのは (どちらかといえば)後退になるでしょう

> 廃止される前に別のやり方をと思い、requestsを試してみたのですが

質問には現在の環境が記してあるけれど, どこまでが変更の許容範囲か不明です  
requests が検討の対象になるのなら, Flask検討がまだ現状に近い気がします

* * *

### CGI と WSGI の資料

他でも述べてあるように, WSGIへの移行が望ましいでしょう

参考: Python 2.7 の頃の (昔の) Python HOWTO: [Python を Web
上で使うには](https://docs.python.org/ja/2.7/howto/webservers.html)  
前半に, CGI と, WSGI について記されている

> CGI プログラムを書くことは可能ではありますが、もはや推奨されません。
> [WSGI](https://docs.python.org/ja/2.7/howto/webservers.html#wsgi)
> (詳しくは後で述べます) では CGI をエミュレートするプログラムを書くことができるので、よりよい選択肢が選べない場合には CGI
> としてプログラムを実行できます。

Wikipedia [Web Server Gateway
Interface](https://ja.wikipedia.org/wiki/Web_Server_Gateway_Interface)

前の項目で CGIはセキュリティー上懸念があることを記しましたが  
WSGI の場合は WSGIサーバーが制限を行うはずで, WSGIアプリはアプリが行うことのみに専念できます

WSGI関連の質問もあり, 参考になるかも

  * [https://ja.stackoverflow.com/questions/43286/cgi-と-uwsgi-について](https://ja.stackoverflow.com/questions/43286/cgi-%E3%81%A8-uwsgi-%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
  * [https://ja.stackoverflow.com/questions/83920/apache2とdjangoプロジェクトをmod-wsgiで紐付けする際のエラーについて](https://ja.stackoverflow.com/questions/83920/apache2%E3%81%A8django%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92mod-wsgi%E3%81%A7%E7%B4%90%E4%BB%98%E3%81%91%E3%81%99%E3%82%8B%E9%9A%9B%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

### WSGI利用のサンプルコード

① [wsgiref --- WSGI
ユーティリティとリファレンス実装](https://docs.python.org/ja/3/library/wsgiref.html)  
ページの最後辺りにサンプルコードが載っています。

② WSGI Tutorialより [Parsing the Request -
Post](http://wsgi.tutorial.codepoint.net/parsing-the-request-post)

  * 2006-2015 と記され, またかなり古いので 少し直したコード  
(WSGI 自体は今でも利用されてるが 動作確認程度のはずで, Django, Flask などの Webアプリフレームワークが主流と思われる)

  * `cgi.FieldStorage()` の代わりの処理も含む

[code]

    from wsgiref.simple_server import make_server
    from urllib.parse import parse_qs
    from html import escape,unescape
    from string import Template
    
    html = """
    <html>
    <body>
       <form method="post" action="">
            <p>
               Age: <input type="text" name="age" value="${age}">
            </p>
            <p>
                Hobbies:
                <input
                    name="hobbies" type="checkbox" value="software"
                    ${checked_software}
                > Software
                <input
                    name="hobbies" type="checkbox" value="tunning"
                    ${checked_tunning}
                > Auto Tunning
            </p>
            <p>
                <input type="submit" value="Submit">
            </p>
        </form>
        <p>
            Age: ${age}<br>
            Hobbies: ${hobbies}
        </p>
    </body>
    </html>
    """
    
    def application(environ, start_response):
    
        # the environment variable CONTENT_LENGTH may be empty or missing
        try:
            request_body_size = int(environ.get('CONTENT_LENGTH', 0))
        except ValueError:
            request_body_size = 0
    
        # When the method is POST the variable will be sent
        # in the HTTP request body which is passed by the WSGI server
        # in the file like wsgi.input environment variable.
        request_body = environ['wsgi.input'].read(request_body_size)
        d = parse_qs(request_body.decode())
    
        age = d.get('age', [''])[0] # Returns the first age value.
        hobbies = d.get('hobbies', []) # Returns a list of hobbies.
    
        # Always escape user input to avoid script injection
        age = unescape(age)
        hobbies = [unescape(hobby) for hobby in hobbies]
    
        response_body = Template(html).substitute({ # Fill the above html template in
            'checked_software': ('', 'checked')['software' in hobbies],
            'checked_tunning': ('', 'checked')['tunning' in hobbies],
            'age': age or 'Empty',
            'hobbies': ', '.join(hobbies or ['No Hobbies?'])
        }).encode()
    
        status = '200 OK'
    
        response_headers = [
            ('Content-Type', 'text/html'),
            ('Content-Length', str(len(response_body)))
        ]
    
        start_response(status, response_headers)
        return [response_body]
    
    httpd = make_server('localhost', 8051, application)
    httpd.serve_forever()
    
[/code]

③ Flask フレームワークの例 (Flask は一例であり, このフレームワークを勧めているわけではない)

  * (developer.mozilla.org) [フォームデータの送信: python_の例](https://developer.mozilla.org/ja/docs/Learn/Forms/Sending_and_retrieving_form_data#python_%E3%81%AE%E4%BE%8B)  
Pythonスクリプトと 2つのテンプレート <https://github.com/mdn/learning-
area/tree/main/html/forms/sending-form-data>

  * `cgi.FieldStorage()` の代わりは `request.form['say']` 辺り

[code]

    $ tree .
    .
    ├── python-example.py
    └── templates
        ├── form.html
        └── greeting.html
    
[/code]

[code]

    from flask import Flask, render_template, request
    app = Flask(__name__)
    
    @app.route('/', methods=['GET', 'POST'])
    def form():
        return render_template('form.html')
    
    @app.route('/hello', methods=['GET', 'POST'])
    def hello():
        return render_template('greeting.html', say=request.form['say'], to=request.form['to'])
    
    if __name__ == "__main__":
        app.run()
    
[/code]

上記のサンプルコード単体での実行は簡単ですが, `mod_wsgi` インストールして Apacheから呼び出すのは難しそうです  
けれどバイナリダウンロード可能かも知れないので参考に

参考: (qiita.com) [FlaskアプリをApache + mod_wsgi on
windowsで動かす](https://qiita.com/TakenoriHirao/items/e28624a62be2ff685123)


CC BY-SA 4.0
2022-12-13T08:31:13.620
92743
2022-12-18T09:48:16.793
2022-12-18T09:48:16.793
43025
43025
92372
answer