15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

COTOHA を りようして ポケモン てきな メッセージに へんかんする ▼

Last updated at Posted at 2020-03-11

COTOHAの せかいへ ようこそ! ▼

この きじは いかの
きかくを みかけたので
               ▼
ちょうせんしようと おもって
かいた きじです
               ▼

【Qiita x COTOHA APIプレゼント企画】COTOHA APIで、テキスト解析をしてみよう!

ポケモン てきな メッセージとは ▼

かの有名なゲーム「ポケットモンスター」(株式会社ポケモンから発売)の画面で用いられるメッセージ表示のことです(下の画像はポケットモンスター赤・緑のゲーム画面より)。画面イメージのアニメーション
要はこれを再現したい、というのが本記事の趣旨です(なんでそんなことをしたいのか?単に私がポケモン好きだからです。2020 年 2 月 27 日、24周年おめでとう!)。

このポケモン的なメッセージ表示には

  • 漢字が使われていない(一部例外を除く)
  • 「スペース」で分かち書きされている

という特徴があります1。今回は任意の日本語文章をこういった雰囲気のメッセージに変換するものを COTOHA を利用することで目指します。

結果サンプル

$ python3 poke_msg.py "ポケットモンスター、縮めてポケモン。この星の不思議な不思議な生き物。空に山に海にポケモンはいたるところでその姿を見ることが出来る。" 
ポケットモンスター
               ▼
ちぢめて ポケモン
               ▼
この ほしの ふしぎな ふしぎな
いきもの
               ▼
そらに やまに うみに
ポケモン は いたる ところで
               ▼
その すがたを みる ことが
できる
               ▼

こんな感じのをつくります。

ソースコード

ソースコードは以下の GitHub リポジトリにあげています。
mkuriki1990/poke_msg - GitHub
ライセンスは MIT License です。

COTOHA API とは ▼

COTOHA API は NTT コミュニケーションズが開発の、日本語辞書を活用した自然言語処理、音声認識 API です。
https://api.ce-cotoha.com/
日本語を解析して、構文解析、キーワード抽出、感情抽出、音声認識などができるスゴイやつです。
cotoha.png
「for Developer プラン」で制限付きですが、無料で利用することができます。ここでは次の記事を参考に Python で使用できるようにしました。
自然言語処理を簡単に扱えると噂のCOTOHA APIをPythonで使ってみた - Qiita

COTOHAで まずは かいせき ▼

Qiita 内にあった COTOHA に関する記事 オレ プログラム ウゴカス オマエ ゲンシジン ナル - Qiita を参考にしました。

この記事は原始人的な(?)言葉になるように、入力された文章を構文解析にかけた後、助詞等を省いて引数に与えた文字列を出力するコードになっていたので、まずはそこから助詞等の単語を省く部分を消してみました。また出力がカタカナだけになっていると雰囲気が出ないので、jaconv ライブラリを用いて、無理やりひらがなに変換します。

実行環境

  • Windows10 (WSL : Ubuntu)
  • Python 3.6.9

最初のコード

元記事のコードをちょっといじったコード(クリックで展開)
pokemon_msg.py

import requests
import json
import sys
import jaconv

BASE_URL = "https://api.ce-cotoha.com/api/dev/nlp/"
CLIENT_ID = "COTOHA Client ID を いれる"
CLIENT_SECRET = "COTOHA Client Secret を いれる"


def auth(client_id, client_secret):
    token_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8"
    }
    
    data = {
        "grantType": "client_credentials",
        "clientId": client_id,
        "clientSecret": client_secret
    }
    r = requests.post(token_url,
                      headers=headers,
                      data=json.dumps(data))
    return r.json()["access_token"]


def parse(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
        "type": "default"
    }
    r = requests.post(base_url + "v1/parse",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()


if __name__ == "__main__":
    document = "君は今、カントー地方への第一歩を踏み出した!"
    args = sys.argv
    if len(args) >= 2:
        document = str(args[1])
   
    access_token = auth(CLIENT_ID, CLIENT_SECRET)
    parse_document = parse(document, access_token)
    result_list = list()
    for chunks in parse_document['result']:
        for token in chunks["tokens"]:
            result_list.append(jaconv.kata2hira(token["kana"]))

    print(' '.join(result_list))

結果

$ python3 pokemon_msg.py "君は今、カントー地方への第一歩を踏み出した!"
きみ は いま かんとー ちほう への だいいっぽ を ふみだ し た

まあまあそれっぽいのがでてきました。

もっと ポケモン てきな ふんいきを ついきゅうする ▼

しかし、実際のポケモンのテキストメッセージはこんなにたくさんスペース区切りは入っていません。品詞はこんなに分割されず、最後の「ふみだ し た」は「ふみだした」と一つの塊でいて欲しいです。またカタカナも表示できるので、もともとカタカナで表記している「カントー」などはそのまま表示したいところです。

区切りスペースの場所を変える

上述の通り、現状ではちょっと区切りのスペースが多すぎます。ゲームでは基本的には文節ごとに区切りスペースが入っているように見えるのと、固有名詞の後には区切りスペースを入れている場合が多いように見えます。API リファレンスを見ると、幸い COTOHA API では chunk_info として文節ごとに読み出せるので、文節ごとに文字列を連結することができます。さらに tokens の中の features を見れば 副品詞 として 固有名詞 が判別できるので、その後にだけ全角スペースを足すように変えてみました。

result_list = list()
for chunks in parse_document['result']:
    text = "" # 空のテキストを用意しておく
    for token in chunks["tokens"]:
        word = jaconv.kata2hira(token["kana"]
        if "固有" in token["features"]:
            text += word + " " # 全角スペースを足す
        else:
            text += word
    result_list.append(text)

カタカナ以外の単語のみひらがなに変換する

またカタカナの単語はそのまま使いたいので、以下のような関数を定義しました。COTOHA では tokens の中の form というのが元の語で、それの読み仮名が kana に入っています。これを比較することで、formkana が一致すればカタカナの語であるし、そうでなければひらがなにして返す関数です。これを間に挟むことで、カタカナの語をカタカナのままで使います。

def conv_kana(token):
    if token["form"] != token["kana"]:
        word = jaconv.kata2hira(token["kana"])
    else:
        word = token["kana"]
    return word

とりあえずここまでのコード

ここまでのコード全体(クリックで展開)
pokemon_msg.py

import requests
import json
import sys
import jaconv

BASE_URL = "https://api.ce-cotoha.com/api/dev/nlp/"
CLIENT_ID = "COTOHA Client ID を いれる"
CLIENT_SECRET = "COTOHA Client Secret を いれる"


def auth(client_id, client_secret):
    token_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8"
    }

    data = {
        "grantType": "client_credentials",
        "clientId": client_id,
        "clientSecret": client_secret
    }
    r = requests.post(token_url,
                      headers=headers,
                      data=json.dumps(data))
    return r.json()["access_token"]


def parse(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
        "type": "default"
    }
    r = requests.post(base_url + "v1/parse",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()

# カタカナ以外の単語のみひらがなに変換する
def conv_kana(token):
    if token["form"] != token["kana"]:
        word = jaconv.kata2hira(token["kana"])
    else:
        word = token["kana"]
    return word


if __name__ == "__main__":
    document = "君は今、カントー地方への第一歩を踏み出した!" # サンプルテキスト
    args = sys.argv
    if len(args) >= 2:
        document = str(args[1]) # 引数があればサンプルと入れ替え

    access_token = auth(CLIENT_ID, CLIENT_SECRET)
    parse_document = parse(document, access_token)
    result_list = list()
    for chunks in parse_document['result']:
        text = "" # 空のテキストを用意しておく
        for token in chunks["tokens"]:

            word = conv_kana(token)
            if token["pos"] == "名詞":
                text += word + " "
            else:
                text += word

        result_list.append(text)

    print(' '.join(result_list))

結果

$ python3 pokemon_msg.py "君は今、カントー地方への第一歩を踏み出した!"
きみは いま カントーちほうへの だいいっぽを ふみだした

だいぶそれらしくなりました。

ながい ぶんしょうを ポケモン てきな メッセージに する ▼

原作ゲームのあるキャラクターの言葉に以下のようなものがあります(勝手に漢字変換、句読点追加しています)。

強いポケモン、弱いポケモン、そんなの人の勝手。本当に強いトレーナーなら、好きなポケモンで勝てるように頑張るべき。

これを変換すると以下のようになります。

$ python3 pokemon_msg.py "強いポケモン、弱いポケモン、そんなの人の勝手。本当に強いトレーナーなら、好きなポケモンで勝てるように頑張るべき。"
つよい ポケモン  よわい ポケモン  そんなの ひとの かって ほんとうに つよい トレーナーなら すきな ポケモン で かてるように がんばるべき

かなり冗長な表示です。ポケモンのゲーム内では、画面幅の問題もあり、テキストは適当な長さ(赤・緑バージョンの場合 16 字)で折り返されています。また句読点のあるところは改行が入っているように見えます。そのあたりを調整します。

句読点の処理

句読点も tokens の中を見ると調べられます。pos に「句点」または「読点」があれば改行を入れるようにします。またメッセージには「!」「?」といった「感嘆符」「疑問符」が入ることがあります。これらは「。」「、」と違ってポケモンではメッセージとして表示されるので、features を調べることで適宜改行とセットで挿入することにします。

result_list = list()
for chunks in parse_document['result']:
    text = "" # 空のテキストを用意しておく
    for token in chunks["tokens"]:
        word = jaconv.kata2hira(token["kana"]
        if "固有" in token["features"]:
            text += word + " " # 全角スペースを足す
        elif token["pos"] == "句点" or token["pos"] == "読点":
            if "疑問符" in token["features"]:
                text += "\n"
            elif "感嘆符" in token["features"]:
                text += "\n"
            else:
                text += "\n"
        else:
            text += word
    result_list.append(text)

文字列の折返し

初代ポケモンはゲームハードとしては GAMEBOY を用いていました。画面解像度は 160x144 ドットしかなく、ポケモンの場合は横方向に最大 16 文字までしか表示させていないようです。したがって、16 字を超える場合は改行して表示するようにしました。最後の result_listjoin するところを以下のように書き換えます。

# print(' '.join(result_list))
line = ""
for word in result_list:
    if len(line) == 0:
        line = word
        newLine = line
    else:
        newLine = line + ' ' + word

    if '\n' in newLine:
        if len(newLine) > 16:
            print(line)
            print(word)
        else:
            print(newLine)
        line = ""
    elif len(newLine) <= 16:
        line = newLine
    else:
        print(line)
        line = word

print(line, end='') # 最後の改行を除いている

ここまでのコード

ここまでのコード全体(クリックで展開)
pokemon_msg.py

import requests
import json
import sys
import jaconv

BASE_URL = "https://api.ce-cotoha.com/api/dev/nlp/"
CLIENT_ID = "COTOHA Client ID を いれる"
CLIENT_SECRET = "COTOHA Client Secret を いれる"


def auth(client_id, client_secret):
    token_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8"
    }

    data = {
        "grantType": "client_credentials",
        "clientId": client_id,
        "clientSecret": client_secret
    }
    r = requests.post(token_url,
                      headers=headers,
                      data=json.dumps(data))
    return r.json()["access_token"]


def parse(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
        "type": "default"
    }
    r = requests.post(base_url + "v1/parse",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()

# カタカナ以外の単語のみひらがなに変換する
def conv_kana(token):
    if token["form"] != token["kana"]:
        word = jaconv.kata2hira(token["kana"])
    else:
        word = token["kana"]
    return word


if __name__ == "__main__":
    document = "君は今、カントー地方への第一歩を踏み出した!" # サンプルテキスト
    document = "強いポケモン、弱いポケモン、そんなの人の勝手。本当に強いトレーナーなら、好きなポケモンで勝てるように頑張るべき。" # サンプルテキスト
    args = sys.argv
    if len(args) >= 2:
        document = str(args[1]) # 引数があればサンプルと入れ替え

    access_token = auth(CLIENT_ID, CLIENT_SECRET)
    parse_document = parse(document, access_token)
    result_list = list()
    for chunks in parse_document['result']:
        text = "" # 空のテキストを用意しておく
        for token in chunks["tokens"]:

            word = conv_kana(token)
            if "固有" in token["features"]:
                text += word + " " # 全角スペースを足す
            elif token["pos"] == "句点" or token["pos"] == "読点":
                if "疑問符" in token["features"]:
                    text += "\n"
                elif "感嘆符" in token["features"]:
                    text += "\n"
                else:
                    text += "\n"
            else:
                text += word

        result_list.append(text)

    line = ""
    for word in result_list:
        if len(line) == 0:
            line = word
            newLine = line
        else:
            newLine = line + ' ' + word

        if '\n' in newLine:
            if len(newLine) > 16:
                print(line)
                print(word)
            else:
                print(newLine)
            line = ""
        elif len(newLine) <= 16:
            line = newLine
        else:
            print(line)
            line = word

    print(line, end='') # 最後の改行を除いている

結果

$ python3 pokemon_msg.py "強いポケモン、弱いポケモン、そんなの人の勝手。本当に強いトレーナーなら、好きなポケモンで勝てるように頑張るべき。"
つよい ポケモン

よわい ポケモン

そんなの ひとの かって

ほんとうに つよい
トレーナーなら

すきな ポケモン で
かてるように がんばるべき

おおっ!

なんだか それっぽい
メッセージに なりましたよ!

メッセージ送りができるようにする

ここまでで、かなりそれっぽい表示ができるようになりました。原作ゲームでは画面に一度にすべてのテキストを表示するのではなく、大体 2 行程度で表示し、ボタンを押すとメッセージを送っていけるような仕組みになっています。これを再現してみることにします。またメッセージ送りができることを示すためにメッセージの端に を表示できるように変えてみます。メッセージ送りは input() を入れて Enter キーの入力を待つことにしました。

line = ""
lineCounter = 0
for word in result_list:
    if len(line) == 0:
        line = word
        newLine = line
    else:
        newLine = line + ' ' + word

    if '\n' in newLine:
        if len(newLine) > 16:
            print(line)
            print(word)
        else:
            print(newLine);
        lineCounter = 2
        line = ""
    elif len(newLine) <= 16:
        line = newLine
    else:
        print(line); lineCounter += 1
        line = word

    if lineCounter >= 2:
        print("               ▼"); input()
        lineCounter = 0

print(line, end='') # 最後の改行を除いている

ここまでのコード

ここまでのコード全体(クリックで展開)
pokemon_msg.py

import requests
import json
import sys
import jaconv

BASE_URL = "https://api.ce-cotoha.com/api/dev/nlp/"
CLIENT_ID = "COTOHA Client ID を いれる"
CLIENT_SECRET = "COTOHA Client Secret を いれる"

def auth(client_id, client_secret):
    token_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8"
    }

    data = {
        "grantType": "client_credentials",
        "clientId": client_id,
        "clientSecret": client_secret
    }
    r = requests.post(token_url,
                      headers=headers,
                      data=json.dumps(data))
    return r.json()["access_token"]


def parse(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
        "type": "default"
    }
    r = requests.post(base_url + "v1/parse",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()

# カタカナ以外の単語のみひらがなに変換する
def conv_kana(token):
    if token["form"] != token["kana"]:
        word = jaconv.kata2hira(token["kana"])
    else:
        word = token["kana"]
    return word


if __name__ == "__main__":
    document = "君は今、カントー地方への第一歩を踏み出した!" # サンプルテキスト
    document = "強いポケモン、弱いポケモン、そんなの人の勝手。本当に強いトレーナーなら、好きなポケモンで勝てるように頑張るべき。" # サンプルテキスト
    args = sys.argv
    if len(args) >= 2:
        document = str(args[1]) # 引数があればサンプルと入れ替え

    access_token = auth(CLIENT_ID, CLIENT_SECRET)
    parse_document = parse(document, access_token)
    result_list = list()
    for chunks in parse_document['result']:
        text = "" # 空のテキストを用意しておく
        for token in chunks["tokens"]:

            word = conv_kana(token)
            if "固有" in token["features"]:
                text += word + " " # 全角スペースを足す
            elif token["pos"] == "句点" or token["pos"] == "読点":
                if "疑問符" in token["features"]:
                    text += "\n"
                elif "感嘆符" in token["features"]:
                    text += "\n"
                else:
                    text += "\n"
            else:
                text += word

        result_list.append(text)

    line = ""
    lineCounter = 0
    for word in result_list:
        if len(line) == 0:
            line = word
            newLine = line
        else:
            newLine = line + ' ' + word

        if '\n' in newLine:
            if len(newLine) > 16:
                print(line)
                print(word)
            else:
                print(newLine);
            lineCounter = 2
            line = ""
        elif len(newLine) <= 16:
            line = newLine
        else:
            print(line); lineCounter += 1
            line = word

        if lineCounter >= 2:
            print("               ▼"); input()
            lineCounter = 0


    print(line, end='') # 最後の改行を除いている

結果

$ python3 pokemon_msg.py "強いポケモン、弱いポケモン、そんなの人の勝手。本当に強いトレーナーなら、好きなポケモンで勝てるように頑張るべき。"
つよい ポケモン
               ▼
よわい ポケモン
               ▼
そんなの ひとの かって
               ▼
ほんとうに つよい
トレーナーなら
               ▼
すきな ポケモン で
かてるように がんばるべき
               ▼

これは実際にコマンドラインで実行すると、一つずつ Enter キーでメッセージが送れるのでもっと雰囲気がでます。

れいがいしょり ▼

冒頭に書いたようにポケモン的なメッセージ表示では

  • 漢字が使われていない(一部例外を除く)

のですが、一部例外があり、それが価格表示の「円」です。唯一ここだけ漢字が使われています。「円」の字をそのまま置換しようとすると「円滑に進む」というような熟語もすべて変わってしまうので困ります。でも COTOHA API では品詞で 助数詞 を判別することができるので、それを使って判別してみることにしました。

〇〇円の判定に助数詞を利用しようと思ったら……?

ところが、これはだめでした。
COTOHA API のリファレンスに倣って「500円」という語を投げると、返ってくるのは以下のような情報です。

$ curl -X POST -H "Content-Type:application/json;charset=UTF-8" -H "Authorization:Bearer [Access Token]" -d '{"sentence":"500円","type": "default"}' "[API Base URL]/nlp/v1/parse"
{
  "result" : [ {
    "chunk_info" : {
      "id" : 0,
      "head" : -1,
      "dep" : "O",
      "chunk_head" : 0,
      "chunk_func" : 0,
      "links" : [ ]
    },
    "tokens" : [ {
      "id" : 0,
      "form" : "500円",
      "kana" : "ゴヒャクエン",
      "lemma" : "500円",
      "pos" : "名詞",
      "features" : [ ],
      "dependency_labels" : [ ],
      "attributes" : { }
    } ]
  } ],
  "status" : 0,
  "message" : ""
}

あれ、名詞……?

期待されていたのは「500」と「円」に分かれて「円」の pos助数詞 になることでした。「151匹」とか「10才」とかだと、ちゃんと「匹」「才」とかが 助数詞 として分離されるのですが、価格はどうやら一塊で 名詞 になってしまうようです。私は言語学に精通していないので、こういった分類にあまり詳しくないのですが、どうやら期待は外れました。

$ python3 pokemon_msg.py "秘密のポケモン、コイキングがなんとたったの500円!どうだい買うかね?"
ひみつの ポケモン
               ▼
コイキングが
なんとたったのごひゃくえん!
               ▼
どう だい かうかね?
               ▼

~~残念ながら金額に関してはあきらめることにします。~~まあこれでも、ゲームのテキストとしてはそんなに違和感ないですかね。

固有表現抽出を使う

(2020/03/12 追記)
コメントで「固有表現抽出を使うと良い」というアドバイスをいただきました(@hanamizuno さんありがとうございます)。確かにこの結果の classMNY かどうかで判定できそうです。

$ curl -X POST -H "Content-Type:application/json;charset=UTF-8" -H "Authorization:Bearer [Access Token]" -d '{"sentence":"秘密のポケモン、コイキングがなんとたったの500円!どうだい買うかね?","type": "default"}' "[API Base URL]/nlp/v1/ne"
{
  "result" : [ {
    "begin_pos" : 3,
    "end_pos" : 7,
    "form" : "ポケモン",
    "std_form" : "ポケモン",
    "class" : "ART",
    "extended_class" : "",
    "source" : "basic"
  }, {
    "begin_pos" : 8,
    "end_pos" : 13,
    "form" : "コイキング",
    "std_form" : "コイキング",
    "class" : "ART",
    "extended_class" : "",
    "source" : "basic"
  }, {
    "begin_pos" : 21,
    "end_pos" : 25,
    "form" : "500円",
    "std_form" : "500円",
    "class" : "MNY",
    "extended_class" : "",
    "source" : "basic"
  } ],
  "status" : 0,
  "message" : ""
}

まずは文章に含まれる「金額表現」である MNY な要素をすべてリストに格納します。

# 金額表現が含まれる語をリストに入れる
def make_pricelist(ne_document):

    pricelist = list()

    for result in ne_document['result']:
        if result['class'] == 'MNY':
            pricelist.append(result['form'])

    return pricelist

このリストに格納された文字列を順番に走査し、金額表現の場合は元の語を返すように、上でつくったカタカナをひらがなに変換する conv_kana 関数を金額表現にも対応させた conv_word として書き換えました。ただし「五十円」のように漢数字の入った金額表現をそのまま返してしまうと、せっかくの雰囲気が台無しなので、それは場合分けしてアラビア數字に変換することにします。Python用 数字 <-> 漢数字 の相互変換ライブラリ「Kanjize」 - Qiitaで紹介されている kanjize ライブラリを使わせていただきました。また半角数字ではなく、全角数字で出力することにします。

# カタカナ以外の単語のみひらがなに変換し、
# 金額表現が含まれる場合は "円" の漢字だけ残して変換する
def conv_word(token, pricelist):

    if len(pricelist) > 0:
        price = pricelist[0]
        if token["form"] == price:
            price = pricelist.pop(0)
            # 漢数字で表現されている場合はアラビア數字に変える
            if not re.search('[0-9].+', price):
                price = str(kanji2int(price.replace("", ""))) + ""

            # 半角数字を全角数字にして返す
            return jaconv.h2z(price, digit=True, ascii=True)

    if token["form"] != token["kana"]:
        word = jaconv.kata2hira(token["kana"])
    else:
        word = token["kana"]
    return word

ここまでのコード

ここまでのコード全体(クリックで展開)
pokemon_msg.py
import requests
import json
import sys
import jaconv
import re
from kanjize import int2kanji, kanji2int

BASE_URL = "https://api.ce-cotoha.com/api/dev/nlp/"
CLIENT_ID = "COTOHA Client ID を いれる"
CLIENT_SECRET = "COTOHA Client Secret を いれる"


def auth(client_id, client_secret):
    token_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8"
    }

    data = {
        "grantType": "client_credentials",
        "clientId": client_id,
        "clientSecret": client_secret
    }
    r = requests.post(token_url,
                      headers=headers,
                      data=json.dumps(data))
    return r.json()["access_token"]


def parse(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
        "type": "default"
    }
    r = requests.post(base_url + "v1/parse",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()

def ne(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
        "type": "default"
    }
    r = requests.post(base_url + "v1/ne",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()

# カタカナ以外の単語のみひらがなに変換し、
# 金額表現が含まれる場合は "円" の漢字だけ残して変換する
def conv_word(token, pricelist):

    if len(pricelist) > 0:
        price = pricelist[0]
        if token["form"] == price:
            price = pricelist.pop(0)
            # 漢数字で表現されている場合はアラビア數字に変える
            if not re.search('[0-9].+', price):
                price = str(kanji2int(price.replace("", ""))) + ""

            # 半角数字を全角数字にして返す
            return jaconv.h2z(price, digit=True, ascii=True)

    if token["form"] != token["kana"]:
        word = jaconv.kata2hira(token["kana"])
    else:
        word = token["kana"]
    return word

# 金額表現が含まれる語をリストに入れる
def make_pricelist(ne_document):

    pricelist = list()

    for result in ne_document['result']:
        if result['class'] == 'MNY':
            pricelist.append(result['form'])

    return pricelist
    

if __name__ == "__main__":
    document = "君は今、カントー地方への第一歩を踏み出した!" # サンプルテキスト
    document = "強いポケモン、弱いポケモン、そんなの人の勝手。本当に強いトレーナーなら、好きなポケモンで勝てるように頑張るべき。" # サンプルテキスト
    document = "秘密のポケモン、コイキングがなんとたったの500円!どうだい買うかね?" # サンプルテキスト
    args = sys.argv
    if len(args) >= 2:
        document = str(args[1]) # 引数があればサンプルと入れ替え

    access_token = auth(CLIENT_ID, CLIENT_SECRET)
    parse_document = parse(document, access_token)
    ne_document = ne(document, access_token)
    pricelist = make_pricelist(ne_document)
    result_list = list()
    for chunks in parse_document['result']:
        text = "" # 空のテキストを用意しておく
        for token in chunks["tokens"]:

            word = conv_word(token, pricelist)
            if "固有" in token["features"]:
                text += word + " " # 全角スペースを足す
            elif token["pos"] == "句点" or token["pos"] == "読点":
                if "疑問符" in token["features"]:
                    text += "\n"
                elif "感嘆符" in token["features"]:
                    text += "\n"
                else:
                    text += "\n"
            else:
                text += word

        result_list.append(text)

    line = ""
    lineCounter = 0
    for word in result_list:
        if len(line) == 0:
            line = word
            newLine = line
        else:
            newLine = line + ' ' + word

        if '\n' in newLine:
            if len(newLine) > 16:
                print(line)
                print(word)
            else:
                print(newLine);
            lineCounter = 2
            line = ""
        elif len(newLine) <= 16:
            line = newLine
        else:
            print(line); lineCounter += 1
            line = word

        if lineCounter >= 2:
            print("               ▼"); input()
            lineCounter = 0


    print(line, end='') # 最後の改行を除いている

結果

$ python3 pokemon_msg.py "秘密のポケモン、コイキングがなんとたったの500円!どうだい買うかね?"
ひみつの ポケモン 
               ▼
コイキングが
なんとたったの500円!
               ▼
どう だい かうかね?
               ▼

できました。

まとめ ▼

かがくの ちからって すげー!
               ▼
いまは ぱそこんつうしんで
にほんご を おくって
               ▼
かいせきした けっかが
みられるんだと
               ▼

もっとそれっぽくするために

割とそれっぽくなりましたが、もっとやりたいことはありました。

助数詞「円」の処理

前述のとおりです。多分数字との組み合わせで条件分岐すれば判定できると思いますが、とりあえず諦めました。
(2020/03/12 追記) : 固有表現抽出機能を使って処理できるようになりました。

英数字の処理

COTOHA API は優秀なので、全て日本語で扱いやすくしてくれます。例えば COTOHAしーおーてぃーおーえっち にしてくれるし、151匹ひゃくごじゅーいっぴき (ひゃくごじゅー「いちひき」ではない)にしてくれたりします。でもどっちかというと、英字やアラビア数字はそのまま表示したほうがそれっぽくできると思います。

かつようほうほう ▼

音声認識と組み合わせて使えば、喋った内容をレトロゲームテキスト風に変換して、映像にオーバーラップさせて表示するとかできそうです。基本的にひらがなとカタカナになるので、子ども向け番組とかに使えるかもしれません(?)

リンク ▼

ライセンス ▼

この文章

この きじの いんようぶぶんと
ぼうとうの ゲームがめんを
               ▼
のぞく ほんぶんは
かんとりーくらぶばいよんてんぜろで
ライセンスしています
               ▼

(この記事の引用部分と冒頭のゲーム画面を除く本文は CC BY 4.0 でライセンスしています。)2

ソースコード

GitHub リポジトリに記載したようにソースコードは MIT License でライセンスしています。
mkuriki1990/poke_msg - GitHub

  1. 最近の作品では漢字が表示できるようになっていたりもしますが、この記事では俗に言う「第 1 世代」である「ポケモン赤・緑」の仕様に合わせようと思います。

  2. COTOHA API では "CC" は「カントリークラブ」になってしまうようです。もちろんここで "CC" は "Creative Commons" のことです。

15
11
2

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
15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?