【Windows】MeCab で形態素解析する Python コード例(出力フォーマット指定)【NEologd】

Python

MeCab で形態素解析する Python コード例を書きました。

mecab.exe出力フォーマットを指定して解析するコード例です。

-F, --node-format の引数を使用します。

これは、たとえばテキストから『名詞めいし』を抽出するときに便利でした。

名詞めいし』は『品詞ひんし』の情報があれば判別できました。

なので、『表層形, 品詞, 品詞細分類1, 品詞細分類2』だけを出力するようにしたら、解析結果のデータを減らすことができました。

そのコード例を書きました。

OS は Windows 10 (64 bit) です。

mecab-ipadic-NEologd から作成した『MeCab 用のユーザー辞書 (.dic)』を使用して、テキストの形態素解析を行いました。

Python バインディングの外部ライブラリは、必要無かったです。

Python の subprocess.run() から mecab.exe を使用して結果を受け取りました。

MeCab マニュアル

辞書の文字コードは UTF-8 を使用しました

MeCab をインストールするときの『辞書の文字コードの選択 (SHIFT-JIS, UTF-8, UTF-16, EUC-JP)』についてです。

自分は UTF-8 を選んでインストールしました。

cmd.exemecab.exe のコンソール上では、UTF-8 形式の辞書の内容が文字化けしてしまいましたが、解析自体は正常にできていました。

cmd.exemecab.exe のコンソール上で、日本語を表示したい場合は、MeCab をインストールするときに『SHIFT-JIS』を選ぶと正常に表示することができました(その代わり、SHIFT-JIS で表せる範囲のテキストしか解析できなくなりました)。

UTF-8 形式でインストールすれば、『UTF-8 で記録したテキスト』も『SHIFT-JIS で記録したテキスト』も解析することができました。

詳細は割愛しますが、文字コードの違いは(エンコーディングの違いは)、Python で吸収しました。

Python で色々な文字コードのテキストを読み込んで、MeCab に渡すときに UTF-8 にエンコードして渡します。

これで、様々な文字コードで記録された日本語のテキストを、自在に解析できるようになりました。

(参考)(docs.python.org) Unicode HOWTO(Unicode 入門)(😊絵文字✨を含めて Python が Unicode や UTF-8 をどのように使用しているか?の説明です)

32 bit 版 MeCab

『MeCab の本体』とドキュメントの場所です。自分が取得した時の MeCab インストーラーは『mecab-0.996.exe』でした。

(taku910.github.io) MeCab (和布蕪)とは – MeCab: Yet Another Part-of-Speech and Morphological Analyzer

64 bit 版 MeCab

64 bit 版の MeCab でも mecab-dict-index.exe が一緒にインストールされましたので、同じ方法でユーザー辞書 (.dic) を作ることができました。

『64 bit 版の MeCab の本体』の場所です。自分が取得した時の MeCab インストーラーは『mecab-64-0.996.2.exe』でした。

(github.com) MeCab 0.996 64bit version

MeCab の引数

mecab.exe に渡す引数ひきすうの説明です。

(taku910.github.io) その他のコマンドラインオプション (-r, --rcfile, -d, --dicdir, -F, --node-format, -b, --input-buffer-size)

--rcfile に指定する『リソースファイル (mecabrc)』の書き方(ユーザー辞書の指定方法)。

(taku910.github.io) 単語の追加方法 ⇒ ユーザ辞書への追加 ⇒ userdic は CSV フォーマットで複数指定可能(2022年2月2日閲覧)

userdic = /home/foo/bar/foo.dic,/home/foo/bar2/usr.dic,/home/foo/bar3/bar.dic

mecab.exe の『出力フォーマット』。'--node-format', '%m\t%f[0,1,2]\n' を書くために使用しました。

(taku910.github.io) 出力フォーマット ⇒ 形態素の表層文字列 (%m, \t, \n, %f[N1,N2,N3...])。

(taku910.github.io) 使い方 ⇒ とりあえず解析してみる ⇒ 出力フォーマット(2022年2月2日閲覧)

表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音

NEologd マニュアル

『NEologd (mecab-ipadic-NEologd) の本体』とドキュメントの場所です。自分は『緑色の Code▼ボタン ⇒ Download ZIP』から取得した『mecab-ipadic-neologd-master.zip』というファイルを使用しました。

(github.com) neologd/mecab-ipadic-neologd: Neologism dictionary based on the language resources on the Web for mecab-ipadic

(github.com) mecab-ipadic-NEologd とは

ユーザー辞書とリソースファイルの作り方

MeCab 用の『ユーザー辞書 (.dic)』と『リソースファイル (mecabrc)』の作り方を書きました。

【Windows】MeCab で NEologd の辞書を作ってインストールする方法【Python】

Python マニュアル

コード例で使用した Python 機能のマニュアルの場所です。

改行コードについて ('\n', '\r\n', '\r')

コード例の中に (改行コードは様々な要因で変わります) と書いたことの補足です。

mecab.exe の標準出力 (stdout) の末尾には、b'EOS\n' というバイト列 (bytes 型) が付いていました(EOS は、たぶん end of sequence の略です)。

(taku910.github.io) MeCab 出力フォーマット "EOS\n"

ですが、それを Windows の Python で文字列 (str 型) にデコードしたら、'EOS\r\n' になりました('\r' が自動的に追加されました)。

そのあたりの Python の挙動の説明は割愛しますが、自分が見た Python マニュアルの場所を紹介します。

(docs.python.org) class io.TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False, write_through=False)

(docs.python.org) universal newlines テキストストリームの解釈法 ('\n', '\r\n', '\r')(改行コード)

(docs.python.org) str.splitlines([keepends])

自分は解析結果をリストにするときに str.split('\r\n') を使用しましたが、str.splitlines() で分割する方法もありました。ただ、自分は厳密に '\r\n' だけで分割してほしかったので、str.split('\r\n') を使用しました。

コード例

Windows の Mecab で、出力フォーマットを指定して形態素解析する Python コード例です。

MeCab の出力データを削減しつつ、テキストから『名詞』を抽出してみました。

Neologd の辞書から生成した『ユーザー辞書 (.dic)』も使用しました。

MeCab は 32 bit 版でも 64 bit 版でも使用できました。

"""
mecab.exe の形態素解析を使用して、テキストから
『名詞』だけを抽出する Python コード例です。

『ノード フォーマット』の引数
(-F, --node-format=STR) を使用します。

Neologd の辞書から生成した『ユーザー辞書』も使用して、
形態素解析を行いました。
"""
from pathlib import Path
import subprocess, sys, datetime

# 実行ファイル (.exe) の場所です。
exe_file = Path(r'C:\Program Files (x86)\MeCab\bin\mecab.exe')
# exe_file = Path(r'C:\Program Files\MeCab\bin\mecab.exe') # 64-bit

# システム辞書のフォルダの場所です。
system_dic_dir = Path(r'C:\Program Files (x86)\MeCab\dic\ipadic')
# system_dic_dir = Path(r'C:\Program Files\MeCab\dic\ipadic') # 64-bit

# rcfile (resource file) の場所です。
# この中に『ユーザー辞書』のファイルパスたちを書いて
# mecab.exe に渡します。
rc_file = Path(r'F:\user_dic\mecabrc')

# (デバッグ)
print(f'(Python) {sys.version}')
print(f'(exe_file) {exe_file}')
print(f'(system_dict_dir) {system_dic_dir}')
print(f'(rc_file) {rc_file}\n')


def main():
    # (1/5) エンコーディング (文字コード) を決めます。
    # MeCab の辞書 (システム辞書 & ユーザー辞書) と
    # 同じエンコーディングを選びます。
    # UTF-8 で辞書を作ったなら 'utf-8' を選びます。
    # SHIFT-JIS で辞書を作ったなら 'shift-jis' を選びます。
    encoding = 'utf-8'

    # (2/5) 文章を用意します。
    src_text = 'ジャパンネット銀行がPayPay銀行に変わりました。'

    # 文字列 (str 型, Unicode コードポイントの文字列) を
    # エンコードして、バイト列 (bytes 型) に変換します。
    src_bytes = src_text.encode(encoding, errors='strict')

    # mecab.exe の『入力バッファサイズ』を決めます。
    # 文章の『バイト列の長さ + 1』の数を指定します。
    len_src_bytes = len(src_bytes) + 1
    # +1 しないと、mecab.exe が標準エラー出力 (stderr) に
    # 以下のエラーメッセージを出しました。
    # input-buffer overflow. The line is split. use -b #SIZE option.

    # (デバッグ) --input-buffer-size を使わなかったときの
    # 文字数の限界例です (MeCab 辞書が UTF-8 の場合です)。
    # ※ 自分の環境で試した結果です ※
    # src_text = 'あ' * 2730 # OK (2730文字) 入力バッファサイズの指定無し。
    #   len(src_text.encode('utf-8')) ⇒ 8190
    # src_text = 'あ' * 2731 # NG (2731文字) --input-buffer-size 8194 で OK。
    #   len(src_text.encode('utf-8')) ⇒ 8193

    # (デバッグ)
    print(f'(src_text) {src_text}')
    print(f'len(src_text) {len(src_text)}')
    print(f'(encoding) {encoding}')
    print(f'len(src_bytes) {len(src_bytes)}')
    print(f'(--input-buffer-size) {len_src_bytes}\n')

    # (3/5) コマンドを作ります。
    # タプル (tuple) でもリスト (list) でも OK です。
    cmd = (
        exe_file,
        '--rcfile', rc_file,
        '--dicdir', system_dic_dir,
        '--node-format', '%m\t%f[0,1,2]\n',
        # '--node-format', '%m\t%f[0]\t%f[1]\t%f[2]\n', # ←上手く区切れないときはこちら
        '--input-buffer-size', str(len_src_bytes),
        )
    # (--node-format の説明)
    # %m : 表層形の文字列です(形態素の表層文字列です)。
    # \t : タブ文字 (Tab) です。通常のエスケープ文字列。
    # %f[0,1,2] : %f[N1,N2,N3...] のことです。
    #   以下の『出力フォーマット』の各列から、必要な列だけを選んでいます。
    #   表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音
    #   https://taku910.github.io/mecab/#install-windows
    # 末尾の\n : 結果を行ごとに分割するために付けました。

    # (以下の書き方でも OK)
    # cmd = (
    #     exe_file,
    #     '-r', rc_file,
    #     '-d', system_dic_dir,
    #     '-F', '%m\t%f[0,1,2]\n',
    #     '-b', str(len_src_bytes),
    #     )

    # (システム辞書だけを使用する場合)
    # cmd = (
    #     exe_file,
    #     '--dicdir', system_dic_dir,
    #     '--node-format', '%m\t%f[0,1,2]\n',
    #     '--input-buffer-size', str(len_src_bytes),
    #     )

    # (補足)『ユーザー辞書 (rc_file)』を使うときは、
    # システム辞書のフォルダも指定しないと、以下のエラーが出ました。
    # param.cpp(69) [ifs] no such file or directory: .\dicrc

    # (4/5) mecab.exe で解析します。
    cp = subprocess.run(
        cmd,
        input=src_bytes,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        timeout=30.0,
        )
    # cp は CompletedProcess の略です。

    # stdout=subprocess.PIPEは、結果を
    # cp.stdout から受け取るために設定しました。

    # stderr=subprocess.PIPE は、mecab.exe の標準エラー出力 (stderr) を
    # cp.stderr から受け取るために設定しました。
    # 例えば mecab.exe は、
    # input-buffer overflow. The line is split. use -b #SIZE option.
    # というエラーメッセージを標準エラー出力に出していました。

    # timeout=30.0 は、一応指定しています。
    # たいていは一瞬で完了するテキストしか扱わないので、
    # 30 秒かかっても終わらないなら異常とみなすことにしました。
    # その時は、exception subprocess.TimeoutExpired が発生しました。

    # (デバッグ) 標準エラー出力の内容を表示します (通常は空文字列のはず)。
    stderr = cp.stderr.decode(encoding, errors='backslashreplace')
    # errors には 'strict', 'ignore', 'replace', 'backslashreplace'
    # などが設定できました。
    print(f'(stderr) {stderr}')

    # (デバッグ) 戻り値を表示します。
    print(f'(returncode) {cp.returncode}')
    # 成功時は cp.returncode = 0 でしたが、エラー時もたいてい 0 でした。

    # (5/5) 解析結果を取得します。
    stdout = cp.stdout.decode(encoding, errors='strict')

    # (デバッグ) mecab.exe の解析結果には、
    # 少なくとも末尾に 'EOS\r\n' があるはず。
    # (改行コードは様々な要因で変わります)
    assert stdout.endswith('EOS\r\n')

    # 改行コード '\r\n' で分割してリストにします。
    # (改行コードは様々な要因で変わります)
    lines = stdout.split('\r\n')

    # たとえば『名詞』だけを取得してみます。
    kws = []
    for line in lines:
        # データの終わり ("EOS\n") なら終了します。
        # https://taku910.github.io/mecab/format.html#:~:text=%22EOS%5Cn%22
        # (補足) result.split('\r\n') で行ごとに分割したので、
        # 'EOS\r\n' ⇒ ['EOS', ''] になっています。
        # (改行コードは様々な要因で変わります)
        # (補足終わり)
        if line == 'EOS':
            break

        # データをリストに直します。
        # 'ジャパンネット銀行\t名詞\t固有名詞\t一般'
        # ⇒ ['ジャパンネット銀行', '名詞', '固有名詞', '一般']
        columns = line.split('\t')

        # 品詞 が '名詞' のものだけを取得してみます。
        if columns[1] == '名詞':
            kws.append(columns)

    # (デバッグ) 結果を表示します。
    print('(結果 ここから)')
    for kw in kws:
        print(kw)
    print('(結果 ここまで)')

    # (デバッグ) cmd の内容を表示します。
    print(f'(cmd)\n{cp.args}')
    return


if __name__ == '__main__':
    print('start')
    start_time = datetime.datetime.now()
    try:
        main()
    finally:
        elapsed_time = datetime.datetime.now() - start_time
        print(f'\n(経過時間) {elapsed_time}')
        print('end')

実行結果

コード例の実行結果です。

(Python) 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
(exe_file) C:\Program Files (x86)\MeCab\bin\mecab.exe    
(system_dict_dir) C:\Program Files (x86)\MeCab\dic\ipadic
(rc_file) F:\user_dic\mecabrc

start
(src_text) ジャパンネット銀行がPayPay銀行に変わりました。
len(src_text) 26
(encoding) utf-8
len(src_bytes) 66
(--input-buffer-size) 67

(stderr)
(returncode) 0
(結果 ここから)
['ジャパンネット銀行', '名詞', '固有名詞', '一般']
['PayPay', '名詞', '固有名詞', '一般']
['銀行', '名詞', '一般']
(結果 ここまで)
(cmd)
(WindowsPath('C:/Program Files (x86)/MeCab/bin/mecab.exe'),
'--rcfile', WindowsPath('F:/user_dic/mecabrc'),
'--dicdir', WindowsPath('C:/Program Files (x86)/MeCab/dic/ipadic'),
'--node-format', '%m\t%f[0,1,2]\n',
'--input-buffer-size', '67')

(経過時間) 0:00:00.022353
end

mecab.exe の出力データを削減しつつ、テキストから『名詞』を抽出することができました。

以上です。

スポンサーリンク
シェアする(押すとSNS投稿用の『編集ページ』に移動します)
フォローする(RSSフィードに移動します)
スポンサーリンク
シラベルノート
タイトルとURLをコピーしました