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.exe
や mecab.exe
のコンソール上では、UTF-8 形式の辞書の内容が文字化けしてしまいましたが、解析自体は正常にできていました。
cmd.exe
や mecab.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
に渡す引数の説明です。
--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) mecab-ipadic-NEologd とは
ユーザー辞書とリソースファイルの作り方
MeCab 用の『ユーザー辞書 (.dic
)』と『リソースファイル (mecabrc)』の作り方を書きました。
⇒ 【Windows】MeCab で NEologd の辞書を作ってインストールする方法【Python】
Python マニュアル
コード例で使用した Python 機能のマニュアルの場所です。
- (docs.python.org)
class pathlib.Path(*pathsegments)
- (docs.python.org)
Path.glob(pattern)
- (docs.python.org)
Path.mkdir(mode=511, parents=False, exist_ok=False)
- (docs.python.org)
PurePath.joinpath(*other)
- (docs.python.org)
str.encode(encoding='utf-8', errors='strict')
- (docs.python.org)
bytes.decode(encoding='utf-8', errors='strict')
- (docs.python.org) 標準エンコーディング
encoding='cp932', 'shift_jis', 'euc_jp', 'utf_8', 'utf_16', ...
- (docs.python.org) エラーハンドラ
errors='strict', 'ignore', 'replace', 'xmlcharrefreplace', 'backslashreplace', ...
- (docs.python.org)
subprocess.PIPE
- (docs.python.org)
exception UnicodeEncodeError
エンコード エラー - (docs.python.org)
exception UnicodeDecodeError
デコード エラー - (docs.python.org)
exception subprocess.TimeoutExpired
- (docs.python.org)
str.endswith(suffix[, start[, end]])
- (docs.python.org)
str.split(sep=None, maxsplit=- 1)
- (docs.python.org)
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs)
- (docs.python.org)
class subprocess.CompletedProcess
- (docs.python.org)
stdout
- (docs.python.org)
stderr
- (docs.python.org)
classmethod datetime.now(tz=None)
- (docs.python.org)
sys.version
改行コードについて ('\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) 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
の出力データを削減しつつ、テキストから『名詞』を抽出することができました。
以上です。