Apacheモジュールwsgiとpythonの連携方法

前回感情分析
https://qiita.com/denka/items/9254b6d2e6873c31056e
をした際に用いたPythonのコードをwsgiを使ってブラウザに表示します。

#目次
・wsgiサーバーの設定ファイル作成
・application関数
・コード直し
・参考

#環境
VirtualBox 6.1.22 r144080 (Qt5.6.3)
Python 2.7.5
Apache 2.4.6

##wsgiの設定ファイル作成
まず/etc/httpd/conf.dにwsgi.confというファイルを作成します。内容は以下です。

```
LoadModule wsgi_module
/home/linuxbrew/.pyenv/versions/3.6.13/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
WSGIRestrictStdin Off
WSGIRestrictStdout Off
WSGIDaemonProcess myapp user=apache group=apache
WSGIProcessGroup myapp
WSGISocketPrefix /var/run/wsgi
WSGIScriptAlias /API  *アクセスしたいファイルのパス*
<VirtualHost *:80>
DocumentRoot *任意のアドレス*
ServerName *任意のドメイン＊
<Directory *任意のアドレス＊>

  Options ExecCGI MultiViews Indexes
  MultiViewsMatch Handlers

  AddHandler wsgi-script .py
  AddHandler wsgi-script .wsgi
  DirectoryIndex index.html app.wsgi

  Order allow,deny
  Allow from all

</Directory>
</VirtualHost>
```
ここではvirtualhostを使って設定しています。ここでwsgiのプロセスはmyappという名前になっていますので、デバッグする際は注意してください。またLoadModule wsgi_moduleの後の/home/linuxbrew/.pyenv/versions/3.6.13/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.soの部分は各自で異なります。```find / -name mod_wsgi*so```でパスを探してください。
設定が完了したらApacheを再起動してください。

##application関数
次に動かすアプリケーションを作ります。まずは簡単な例を示します。

```
def application(environ, start_response):
    body = b'Hello world!\n'
    status = '200 OK'
    headers = [('Content-type', 'text/plain')]
    start_response(status, headers)
    return [body]
```
このようなファイルを作り、wsgi.confがこのファイルを参照するようにWSGIScriptAliasの後にパスを書きます。

```
Hello world!
```
これをブラウザで表示するとこのように表示されます。
このapplicationという関数でファイル中の他の関数で出した値を表示するのが今回の目的です。またapplication関数を除いてpythonファイルを実行しようとするとエラーが出るので、application関数の存在は必須です。

##コード直し
感情分析のコードにapplication関数を足します。

```
def application(environ, start_response):
    status = '200 OK'
    gutenberg = "https://www.gutenberg.org/files/14469/14469-h/14469-h.htm"
    a = main(gutenberg, "bunsho")
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)↲
    return [a]↲
```
gutenbergというのは文章のソースで、mainという関数はその文章のスコアを出す関数です。
次にmain関数に注目します。main関数の返り値は下のmain関数のままでは返り値の形式に関するエラーが出てきてしまいますので書き換えなければなりません。

```
def main(url, title):
    sentences = url
    emotional_words = get_emotional_words()
    count_emotions = count_emotional_words(sentences, emotional_words)
    emotional_kanji = ["貪食", "淫蕩", "金銭欲", "怒り", "悲嘆", "怠惰", "虚栄", "高慢"]
    labels = list(emotional_kanji)
    values = count_emotions
    print(labels)
    print(count_emotions)
```
　返り値のエラーは具体的にいうとapplication関数の返り値がbyte形式でなければならないというものです。そのためにmain関数の返り値をbyte形式にします。またwebで実行するときファイル内のprint文はエラーとして実行されるので注意してください。web上に表示はされません。

```
def main(url, title):
    sentences = url
    emotional_words = get_emotional_words()
    count_emotions = count_emotional_words(sentences, emotional_words)
    emotional_kanji = ["貪食", "淫蕩", "金銭欲", "怒り", "悲嘆", "怠>惰", "虚栄", "高慢"]
    labels = list(emotional_kanji)
    values = count_emotions
    arr = count_emotional_words(sentences, emotional_words)
    length = len(arr)
    # 開始位置を指定
    n = 0
    # 分割する変数の個数を指定
    s = 1
    extension_1 = []
    # 配列を指定した個数で分割していくループ処理
    for i in arr:
        c = arr[n:n+s:1]
        n += s
        extension_1.append(i)
    # カウント数が配列の長さを超えたらループ終了
        if n >= length:
            break↲
    intA = extension_1
    intA = [str(i) for i in intA]
    strA = ",".join(intA)
    strB = strA.encode()
    return strB
```
main関数の返り値をデータベースに保存しておくために関数の外にprint文を置きます。関数の外におけばprint文は機能します。byte型式にした値をstring型式に戻し、それをsplitで値を分けます。print文はwsgiでは働かないようですがターミナルでは正常に表示されます。ここではphpのファイルでpythonファイルが導出した返り値を保存しています。詳しくは以下です。

返り値をstring型式に戻してsplitで値を分け、print文表示

```
main(gutenberg, "bunsho")
test2 = test.decode()
test3 = test2.split(",")
print(test3)
```
データベースに保存するコード

```
<?php
    exec("/usr/bin/python3 ファイル名１ ファイル名２", $outputs, $state);
    $output = preg_replace('"[\[\]]"','',$outputs);
    $jsonstr = json_encode($output);
```

##参考
https://blog.hirokiky.org/entry/2018/09/30/183840
https://tihiro.hatenablog.com/entry/2017/10/30/201730
https://www.toptal.com/python/pythons-wsgi-server-application-interface
https://qiita.com/e10persona/items/7a7643b266c2bdfbf7d0