LoginSignup
5
2

More than 1 year has passed since last update.

Python (Flask) で形態素解析をするAPIを作ってGlitchで公開する

Last updated at Posted at 2021-02-19

概要

  • Python(Falsk)で形態素解析で文章内の単語数を数えるAPIを作ります
  • APIだけではなくJinja2を使用して取得結果を画面に表示します
  • GlitchというNode.jsのアプリを公開するためのサービスで公開します

成果物

  • 文章を入力すると文章内の単語の数を表示します
  • FlaskとJinja2のみでウェブとして動作させています
  • 実際に動作する公開先のリンク
  • プロジェクト(ソースコードなど)を確認できるリンク

スクリーンショット

文章入力画面

image.png

結果画面

image.png

※単語の数が1件のモノが下に続いています

形態素解析するAPI

APIのURL

以下のようにAPIとしても利用できます。
こちらの【リンク】で以下と同じ内容を実行できます。

https://flask-nagisa.glitch.me/morpho_api?text=近年、ITとともにICT(Information and Communication Technology=情報通信科学)という言葉も使われるようになりました。文部科学省の資料にも「学校現場のICT化」といった形で登場する言葉です。

APIの結果

image.png

使用するフレームワークや用語の説明

形態素解析とは

  • 文章内の品詞や単語などに判別することです
  • 形態素解析するライブラリはいろいろあります
  • 今回はnagisaという軽量のライブラリを使用しています
  • Wikipediaでは以下ように記載されています

形態素解析(けいたいそかいせき、Morphological Analysis)とは、文法的な情報の注記の無い自然言語のテキストデータ(文)から、対象言語の文法や、辞書と呼ばれる単語の品詞等の情報にもとづき、形態素(Morpheme, おおま かにいえば、言語で意味を持つ最小単位)の列に分割し、それぞれの形態素の品詞等を判別する作業である。

Flaskとは

  • Pythonの軽量のフレームワークです
  • 今回はバックエンドのAPIの作成がメインです
  • フロントエンドの表示にはJinja2を使用します
  • Wikipediaでは以下ように記載されています。

Flask(フラスク)は、プログラミング言語Python用の、軽量なウェブアプリケーションフレームワークである。 標準で提供する機能を最小限に保っているため、自身を「マイクロフレームワーク」と呼んでいる。 Werkzeug WSGIツールキットとJinja2テンプレートエンジンを基に作られている。

準備

  • まずはGlitchのアカウントを作成します
  • GlitchでFlaskが動作するプロジェクトが公開されています
  • そのプロジェクトをベースに新しいAPIや画面の表示などの機能を追加します
  • https://glitch.com/edit/#!/flask-python3
  • Flask以外にもRailsのプロジェクトもあります
  • 上記のプロジェクトはベータであるため本番稼働というよりお試しのような形での利用が推奨です

flask-python3をベースに自分のプロジェクトを作成する

  • 上記のflask-python3のプロジェクトからRemix Projectを選択します
  • 左上のプロジェクト名の下矢印から選択できます
  • 自分のプロジェクトとして作成できたら機能を追加していきます

image.png

ディレクトリ構成とソース

  • ディレクトリ構成はGlitch上にあるファイル・フォルダ全てを記載しています

ディレクトリ構成

.
├── templates
│       ├── header.html
│       ├── index.html
│       └── morpho.html
├── .env
├── README.md
├── glitch.json
├── requirements.txt
└── server.py

ソースコード

server.py

ルーティングの設定

  • ルーティングでアクセスされたときのURLを設定します
  • API単体としても使えるよう/morpho_apiでJsonの結果を取得することできます
  • URLのルート/ではリクエストのGETPOSTで表示する画面を変えています

GETとPOSTによる処理

  • GETの場合は文章入力画面となるindex.htmlを表示します
  • POSTの場合は結果画面となるmorpho.htmlを表示します

単語を数える処理

  • 単語数を数える処理はcollections.Counterで簡単に実装できます
  • その後にsortedの処理で並び替えています
  • 最終的に結果を返す部分では内包表記で記載しています
  • 並び替えをするために違うオブジェクトに格納しているので何とかしたいところ・・・
from flask import Flask, request, render_template
import collections
import nagisa

app = Flask(__name__)

@app.route("/morpho_api")
def morphological_analysis(document='nagisaを使った形態素解析をして単語を数えるツールです。解析後に単語数を視覚的に表示します。'):
  if request.args.get('text'):
    document = request.args.get('text')
  nagisa_result = nagisa.tagging(document)

  words_list= []
  for index, tag in enumerate(nagisa_result.postags):
    nagisa_word = nagisa_result.words[index]
    if tag == '名詞' and not nagisa_word.isnumeric():
      words_list.append(nagisa_word)

  count_dict = dict(collections.Counter(words_list))
  sorted_result = sorted(
    count_dict.items(),
    key =  lambda kv:(kv[1], kv[0]),
    reverse=True
  )
  max_count = max(count_dict.values())

  return {
    'input': document,
    'max_count': max_count,
    'result': [{'word': result[0], 'count': result[1]} for result in sorted_result]
  }

@app.route("/", methods=['GET', 'POST'])
def index():
  if request.method == 'GET':
    return render_template('index.html')

  if request.method == 'POST':
    if request.form['document']:
      morpho_list = morphological_analysis(document=request.form['document'])
      input_text = morpho_list['input']
      morpho_result = morpho_list['result']
      max_count = morpho_list['max_count']
      return render_template(
                            'morpho.html',
                             input_text=input_text,
                             morpho_result=morpho_result,
                             max_count=max_count
                            )
    return render_template('morpho.html')

@app.route('/test')
def test():
  return 'test'

if __name__ == "__main__":
  app.run()
  # app.run(debug=True)

templates/header.html

  • ヘッダー部分は共通なので別のファイルに分けています
  • Jinja2でもvue.jsのようなコンポネント的な使い方できます
  <head>
    <title>【flask + jinja2】形態素解析による単語を数えるツール</title>
    <meta charset="utf-8" />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css"
    />
  </head>

templates/index.html

  • URLのトップにアクセスしたときに表示されるページです
  • このページからPOSTで単語の数を数えたい文章を送ります
<!DOCTYPE html>
<html lang="ja">
  {% include "header.html" %}
  <body>
    <section class="hero is-primary is-fullheight">
      <!-- Hero content: will be in the middle -->
      <div class="hero-body">
        <div class="container has-text-centered">
          <p class="title">
            形態素解析で単語カウント
          </p>
          <form action="/">
            <textarea
              name="document"
              class="textarea"
              placeholder="単語を数えたい文章を入力して「実行」ボタンをタップ"
              rows="10"
            ></textarea>
            <button
              type="submit"
              formmethod="post"
              class="button mt-4 is-fullwidth"
            >
              実行
            </button>
          </form>

        </div>
      </div>
    </section>
  </body>
</html>

templates/morpho.html

  • 上記のPOST後の表示されるページです
  • 文章内の単語をキーに単語の数のリストを元に結果を表示します
  • 用途的には微妙ですがグラフっぽいのはプログレスバーです
<!DOCTYPE html>
<html lang="ja">
  {% include "header.html" %}
  <body>
    <section class="hero is-primary is-fullheight">
      <!-- Hero content: will be in the middle -->
      <div class="hero-body">
        <div class="container has-text-centered">
          <p class="title">
            形態素解析して単語を数えた結果
          </p>
          {% if input_text %}
          <div class="has-text-left mt-5 mb-5">
            <label class="label is-size-5">入力されたテキスト</label>
            <p class="subtitle has-text-black">
              {{input_text}}
            </p>
          </div>
          {% else %}
          <div class="has-text-left mt-5 mb-5">
            <label class="label is-size-5">テキスト未入力</label>
            <p class="subtitle has-text-black">
              テキストを入力して実行してください。
            </p>
            <p class="subtitle has-text-black">
              なお、誠に申し訳ありませんがJavaScriptは書いていないので事前の入力チェックはありません。
            </p>
          </div>
          {% endif %} {% for result in morpho_result %}
          <div class="has-text-left mt-3">
            <label class="label is-size-5"
              >{{ result['word']|e }}: {{ result['count']|e }}</label
            >
            <progress
              class="progress"
              value="{{ result['count']|e }}"
              max="{{ max_count|e }}"
            ></progress>
          </div>
          {% endfor %}
        </div>
      </div>
    </section>
  </body>
</html>

requirements.txt

  • nagisaを追加してPythonで使用できるようにします
  • 追加すると裏でpip installでインストールされます
Flask
nagisa

glitch.json

※特に編集はせず元のプロジェクトのままの内容です

{
  "install": "pip3 install --user -r requirements.txt",
  "start": "PYTHONUNBUFFERED=true python3 server.py",
  "watch": {
    "ignore": [
      "\\.pyc$"
    ],
    "install": {
      "include": [
        "^requirements\\.txt$"
      ]
    },
    "restart": {
      "include": [
        "\\.py$",
        "^start\\.sh"
      ]
    },
    "throttle": 1000
  }
}

懸念点

ディスク容量がギリギリ

  • glitchで作成した環境の容量が200MBまでです
  • nagisaのインストール後は180MBとギリギリです
  • 常にwarningの状態になりますがFlaskなどは稼働します
  • 本番稼働的な形では若干リスクがあるので注意が必要です

本来はNode.jsがメイン

  • 基本的にGlitchはNode.jsがメインです
  • 裏技のようにPythonやRubyなども動作させることができます
  • 若干非公式感も否めないのでお試し程度の利用がよさそうです

無料枠ではいろいろと制限がある

  • しばらく使っていないと環境がスリープ状態になります
  • スリープ状態で公開しているURLにアクセスすると稼働状態まで時間が掛かります
  • チャットボットのような即座に返すようなAPIには不向きです

ふりかえり

  • Glitchでサクっと作って公開できるのは楽しい
  • 無料でPythonを手軽に動作させる環境ってGlitchくらい?
  • 形態素解析の処理とかはPythonは速い(Kuromoji.jsが遅い?)
  • フロントエンドの表現はPythonだけだと厳しい
5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2