ロリポップサーバーにてCGIをPython3で運用したかった

こんにちは。
このブログはロリポップのレンタルサーバーで運用されているんですが、「レンタルサーバーならCGI動かせるじゃん。cron使えるんだ、使ってみよ」という軽い好奇心でCGIを作成しようとしたら思いの外苦戦したので詰まった点をまとめておきます。「言語?Pythonを前にちょろっと触ったしPython3だな! 何と言っても流行ってるしな!」というテンションでPython3選んだらめちゃくちゃ苦戦した……。
私のようにCGI作成は1日で終わったけどサーバーで動かすだけで3日間かかったという人間がいなくなりますように……!

スポンサーリンク

はじめに

私はふだんJavaしか書いていません。そしてCGIは以前大学の授業でちょっと触ったくらい(しかもその時の言語はperl)なので、以下に書いてることは自分自身が芯から理解しているわけではないことをご了承ください。

HelloWorldが出ない、日本語が出ない

最初に書いておきますが、わりときちんと書いてるのに500エラーになる!はパーミッションの間違いの可能性があります。また、cgitbをインポートして有効にした後に、Content-Type:でhtmlを指定しないとエラーになります。
https://lolipop.jp/manual/hp/cgi/
ロリポップのパーミッションは以上を参考にしてください。正しく設定しないと本当に動作しません。適当に777でいいやとかしないように。

#!/usr/local/bin/python3.4
# coding:utf-8
import cgitb
cgitb.enable()
print("Content-Type: text/html; charset=UTF-8\r\n")
print("<html><head><title>Title</title></head><body>")
print("Hello!")
print("</body></html>")

ファイル名は適当にいいですが拡張子は.cgiです(.pyで動くかどうかは不明。たぶん動くと思う)。これが動作しない場合はおそらくパーミッションエラー(ロリポップ以外のサーバーの場合はきちんとpythonの言語パスを確認してください(1行目))。
ちなみに文中のcgitbについては公式のドキュメントを参考にしてください。噛み砕いて言うとCGIとして使うときにエラーメッセージをHTMLで出力してくれるよという代物。
そしてこれ、Hello!の部分をこんにちわ!とするだけで動作しなくなります。2バイト文字(日本語)が入ってくるだけで動かなくなるんです。
色々調べた結果、動作するコンピュータのロケールが取得できず、入出力の文字コードがANSI_X3.4-1968となっているのが原因の模様。Apacheの設定をいじれと書いてあるページもありますが、レンタルサーバーではいじれません(最初は.htaccessの設定かと思ったけれど改善せず)。
いろんなページを巡ってソースコードを切り貼りした結果、日本語でも動作するようになったのが以下のコード。

#!/usr/local/bin/python3.4
# coding:utf-8
import cgitb
import sys ,io
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')
cgitb.enable()
print("Content-Type: text/html; charset=UTF-8\r\n")
print("<html><head><title>タイトル</title></head><body>")
print("こんにちわ!")
print("</body></html>")

参考および引用元のサイト様(のページ):404 Blog Not Found | 備忘録 – #python3 で sys.std(in|out|err) の encoding を強制する
記事の一番後ろに私が参考にしたサイト様の一覧を載せておくのでこれで動かない場合はそちらを参考にしてください。またこの問題についてもっと理解したいという人もそちらを見ることをお勧めします。

ファイルの書き換えができない

私が作成したCGIは一定時間ごと(cron)で動作し、どこどこのページから情報を取得してtxtファイルにまとめるというもの。

text = ("なにかしらの要素")
f = open("A.txt", 'w')
f.write(text)
f.close()

A.txtはcgiと同じディレクトリにあるとします。
ローカル環境では問題ないのですが、サーバー上で運用するには問題が2点。

  1. 文字コードを指定していないため、出力時にエラーになる。
  2. ファイルの参照は相対参照でもいいけど現在のディレクトリを書く必要がある。

なので以下の様に書きましょう。

text = "なにかしらの要素"
f = open("./A.txt", 'w' ,encoding='utf-8')
f.write(text) # 引数の文字列をファイルに書き込む
f.close() # ファイルを閉じる

ディレクトリは./から書くか、あるいはいっその事絶対参照にしてもいいかもしれないです。ルートパスはcron設定のページに書かれています。

ちなみにこのA.txtが文字化けしてるという人はブラウザのテキストエンコーディングをUTF-8にしましょう。

参考サイト様

http://methane.hatenablog.jp/entry/20120806/1344269400
http://blog.livedoor.jp/dankogai/archives/51816624.html
http://lab.knightstyle.info/%E7%A7%81%E3%81%8Cpython3%E3%81%A7unicodeencodeerror%E3%81%AA%E3%81%AE%E3%81%AF%E3%81%A9%E3%81%86%E8%80%83%E3%81%88%E3%81%A6%E3%82%82%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E6%96%87%E5%AD%97/
http://chidipy.jpn.com/topics/?p=309