PHPがメインの経験者が、Pythonを始めて３日目の話

#はじめに
WEBは主に「JavaScript、PHP、HTML」で「PerlとCgi」はほんの少し程度しか書いて来なかったので、正直言ってPythonの位置付けが今でも良く分かっていないのです。Pythonをちょっと書き出したのが2019年11月20日で、書いてメモとしてアップして親切な方に色々とご指導を受けました。でもWEBアプリを作る方法が分からず、ネットで調べて気付いた事があります。
##PHPやJavaSvriptのようにPythonは動かない。
JavaScriptからPythonにデータを受け渡すプログラムを自分が管理しているVPSで動かしたいのです。これは正に他の方のパクリなのです。（引用させていただいた方はコードの直前に表記しました）**post.html で文字を送って receive_post.py で受け取りたい**のですが、cgiが動くだけのフォルダでは**「Internal Server Error」**となってしまいました。PHPばかりだとはこんな事が分からないのです。Pythonのコード名をブラウザでそのまま書いては駄目なのかなと、悩んで２日目でやっと動きましたので顛末を書きます。参考にした、以下のサイトではどうも駄目でした。

[Pythonを使ったWebサイトは百円で運用できる](https://news.mynavi.jp/article/zeropython-43/)
[PHP/CGIについて](http://faq.sakuratan.com/wiki/wiki.cgi?cgi%a4%c8php#i4)
######レンタルサーバではPythonは動いても、エラーの内容が分かりません
さくらサーバーとスタードメインは他に必要があって契約してますが、どうしても動かなくて調べ続けたらサポートも理由が分からないという記述を発見して、これ以上は時間の無駄だと諦めました。最終的に ConohaのVPS で動いたのですが、まずはお約束の「こんにちは」を表示するところからです。拾ったサンプルは以下となります。レンタルサーバではエラーの error.log が読めない場合が多いので避けた方が良さそうです。

``` hello.py
#!/usr/bin/python3.6

# 日本語を扱うために必要な設定 --- (*1)
import os, sys, io, cgi
sys.stdin =  open(sys.stdin.fileno(),  'r', encoding='UTF-8')
sys.stdout = open(sys.stdout.fileno(), 'w', encoding='UTF-8')
sys.stderr = open(sys.stderr.fileno(), 'w', encoding='UTF-8')

out = lambda msg: print(msg, end="\r\n")

# ヘッダの出力 --- (*2)
out("Content-Type: text/html; charset=utf-8")
out("")

# HTMLの出力 --- (*3)
out("<html><meta charset='utf-8'><body>")
out("<h1>こんにちは</h1>")
out("</body></html>")
```

**Pythonの注意点（プログラムコード本体）**
１．文字コードは UTF-8
２．改行コードは LF
３．BOMを付けない
４．プロパティは 705 755 777 等で動きます。
**Pythonのモジュールのパスに注意**
５．一行目の環境に注意（環境のパスを疑うこと）
次の error.log のようにファイルが見つからないエラーになります。この一行のリンクを辿る事で python3.6 というファイルが発見できますが、ここには同じ python のファイルが幾つかありました。その中で動作してくれたのが、この python3.6 だったという訳です。

``` error.log
[Fri Nov 22 11:53:54.070841 2019] [cgid:error] [pid 27144] (2)No such file or directory: AH01241: exec of '/var/www/******/******/hello.py' failed
```

######Pythonの罠（その１）コマンドラインで簡単に動くこと
最初、コンソールでPythonを起動してコマンドで色々とやっていた時期があります。これが実に楽で、拾ったプログラムとかも普通に動きますので簡単に考えていました。このインタープリターである程度の技術計算など普通に出来てしまうというのは有り難くも困りものです。その寂しい知識で目的のWEBアプリに入る所で混乱しました。
######Pythonの罠（その２）複数のバージョンが入ってしまうこと
これはミスなのでしょうが、Pythonの環境を整備しているつもりが、複数のバージョンのPythonが入ってしまう問題です。実は hello.py の一行は #!/usr/bin/python3.6 としていますが、同じフォルダに python python2.7 python3.6 が存在します。でも3.6以外に設定した場合は先のエラー（error.log）が出力されて動きません。その時にブラウザにはお約束の「Internal Server Error」が表示されます。この問題解決は大変そうなので後で調べようと思いますが、これは賢者が教えてくれるかもしれません。一応、調べましたが直接不要なバージョンを消すなどの方法が多かったようです。

######ここまでのセオリーを守って無事に動作した post.html と recieve_post.html
[以下の引用元、バタヤしさん](https://bayashita.com/p/entry/show/255)

``` post.html

<html lang="ja">
<head>
<meta charset="UTF-8">
</head>
<body>
 
<form method="post" action="receive_post.py">
    <input type="text" name="your_name" />
    <input type="submit" />
</form>
 
</body>
<html> 
```

``` recieve_post.py

#!/usr/bin/python3.6
# -*- coding: utf-8 -*-
 
import cgi
import sys
import io
 
# 日本語を受信時にエラーにならないようにする為に必要。
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

form = cgi.FieldStorage()
 
# 入力チェック（必要な変数が送信されていない場合はエラー。）
if 'your_name' not in form:
    print('Content-type: text/html; charset=UTF-8')
    print('')
    print('your_name フィールドが送信されていません。')
    sys.exit()
 
# your_name の値を取得して変数にセット。
# 値が入力されていない場合は「匿名」を設定。
# your_name が複数ある場合は先頭の値を取得。
your_name = form.getfirst('your_name', '匿名')

# テキストファイルとして内容を出力
print('Content-type: text/html; charset=UTF-8')
print('')
print(your_name)
```
ブラウザの入力データを Python が受け取ってくれる処理は flask を利用した方法が多く見られますが、ここまでは hello.py すらエラーで動かなかったので、試す時間も無く来ています。flask も見つからなくてエラーになりそうです。（続く）

####三日目ですが、少し足を伸ばすことにsubprocessで(wget curl)
PCへダウンロードしないでブラウザでファイルを読む機能をPythonで開発しようとしている理由はPythonの理解と同時に今まで気付かなかった事が恐らくあるだろうという興味からです。同じ環境では成長は無いはずなので、PHPとPythonを行き来する事で生まれる世界に期待しています。
###### wget を subprocess で動かしてみました。
最初にコマンドラインで wget を実行してみました。

``` Python3.7
# （import subprocess）をやらないと NameError: name 'subprocess' is not defined となります。これで「sjd23d_mn.pdf」が落ちます。
>>> import subprocess
>>> subprocess.run(["sudo","wget","http://www.sharp.co.jp/support/refrigerator/doc/sjd23d_mn.pdf"])
```
######ブラウザで動く python のプログラムに subprocess は実装できるのか（出来るらしいけど出来てません）
実はコマンドラインで動くプログラムで subprocess は実装して動くところまでは確認できましたが、これを既に用意した hello.py に組み込んで動くようになるかが現在の課題です。ちょっと検索しても出て来ないので、あまり需要は無いのでしょうか？以下のような wget.py を実験的に加筆して動かしてみました。このコードで ls の方は動きますが wget はファイルが未だに取れていません。これはダウンロードが認証されないのかもしれませんので、少し根気よくやってみましょう。

######この時点（午前１時）で解決しつつありますので続きを開始します。
最初に文字列の処理を昨日に教えて貰った正規表現と先ほど調べた方法で記述してみます。保存先のフォルダ（data）のパーミッションを 777 とする事を忘れてました。これはPHPでも同じです。これでデータの取得に成功しました。

``` word.py

#最初のリンクアドレスは前回の物を流用します
string = "http://www.sharp.co.jp/support/refrigerator/doc/sjd23d_mn.pdf?productId=SJ-D23D&_ga=2.1612.1531209133-1752366186.1522914385"
# これを正規表現で?から後ろをバッサリ消します。
string = ''.join(string[:list(string).index('?')])
# 以下のようになります。
print(string) # http://www.sharp.co.jp/support/refrigerator/doc/sjd23d_mn.pdf
# 次にPHPでの mb_basename() を Python では以下になると分かりました。
import os # os を呼び込みます
orname = os.path.basename(string)
# 以下のようになります。
print(orname) # sjd23d_mn.pdf
```

######ここまでの結果を実装してテストプログラムを用意します。

``` wget.py

#!/usr/bin/python3.6
# -*- coding: utf-8 -*-

import cgi
import sys
import io
import os
import subprocess

# 日本語を受信時にエラーにならないようにする為に必要。
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

form = cgi.FieldStorage()

# 入力チェック（必要な変数が送信されていない場合はエラー。）
if 'your_name' not in form:
    print('Content-type: text/html; charset=UTF-8')
    print('')
    print('your_name フィールドが送信されていません。')
    sys.exit()

string = "http://www.sharp.co.jp/support/refrigerator/doc/sjd23d_mn.pdf?productId=SJ-D23D&_ga=2.1612.1531209133-1752366186.1522914385"
string = ''.join(string[:list(string).index('?')])
orname = os.path.basename(string)
proc = subprocess.run(["wget","-O","data/"+orname,string])

# your_name の値を取得して変数にセット。
# 値が入力されていない場合は「匿名」を設定。
# your_name が複数ある場合は先頭の値を取得。
your_name = form.getfirst('your_name', '匿名')

# テキストファイルとして内容を出力
print('Content-type: text/html; charset=UTF-8')
print('')
print(your_name)
```
以上で普通にデータは取れるようになりましたが、リダイレクトへの対応やブラウザらしく振る舞う等の処理も書き込む仕上げを明日から行います。来週は忙しくなるようなので、土日にサクッと書きます。

######転送したファイルを処理する為に実装したい機能を整理してみましょう
大半が Linux の機能を利用するだけなので PHP を Python に置き換える感じでは無いかもしれません。

- Linux（UNIX）の FILE でファイルのフォーマットを調べます。
- pdftotext これは PDF をテキストに変換します。
- pdftoppm これも PDF を画像に変換する機能です。
- unar zip lzh の解凍です。
- selenium サーバに実装したブラウザでパスワードが掛かったサイトにログイン。
- 処理中に出来たディレクトリやファイルの削除。ファイル名から空白の削除。
- サニタイジングは簡単に出来るでしょう。

######以上の処理方法が分かればサクサク書けそうです。