はじめに
Mecabとは形態素解析エンジンで、要するに文章を解析するソフトである。(僕が言うまでもないか。) googleやyahooにも使われてるらしく、昨日は素晴らしいの一言である。そして今回は、mecabをpythonから使いやすくしたい。python-mecab というライブラリがあって調べると重要なもののようだが、素人にはよくわからない。使いやすくなってるようには見えないので一応書いてみる。
Mecabのホームページ
コード
import subprocess as sp
import os
import sys
import random
if "nt" in sys.builtin_module_names:
ENCODING="sjis"
TYPE_COMMAND="type"
else:
ENCODING="utf8"
TYPE_COMMAND="cat"
def tmpf():
fname="__mecab_tmp{0}.txt".format(random.randint(4545,46494))
return fname if os.path.exists(fname) else tmp()
class Mecab:
def __init__(self,text):
self.text=text
def __call__(self):
return Mecab.__analize__(self.text)
@staticmethod
def __analize__(text):
tmp=tmpf()
with open(tmp,"wb") as f:
f.write(text.encode(ENCODING,"ignore"))
try:
for line in sp.check_output("type {0} {1} | mecab".format(TYPE_COMMAND,tmp),shell=True).decode(ENCODING,"ignore").split("\n"):
data=line.rstrip().split("\t",1)
if not data[0]:
pass
elif data[0]=="EOS\r":
yield ("EOS",None)
else:
data__=data[1].split(",")
yield (data[0],data__)
except:
pass
os.remove(tmp)
@staticmethod
def __getDataAsSentence__(text): #res list include data
sentence=list()
for word,data in Mecab.__analize__(text):
if word=="EOS" or (data and "\\xe3\\x80\\x82" in str(word.encode())):#str(data[1].encode())=="b\'\\xe5\\x8f\\xa5\\xe7\\x82\\xb9\'"): #KUTEN
yield sentence
sentence=list()
else:
sentence.append((word,data))
@staticmethod
def getDataAsSentence(text):
for sentence in Mecab.__getDataAsSentence__(text):
yield "".join(map(lambda a:a[0],sentence))
@staticmethod
def getWords(text):
for word,data in Mecab.__analize__(text):
if word=="EOS" or (data and "\\xe3\\x80\\x82" in str(word.encode())):
continue
yield word
少し複雑にしてしまったが、ご愛敬。
まず
if "nt" in sys.builtin_module_names:
ENCODING="sjis"
TYPE_COMMAND="type"
else:
ENCODING="utf8"
TYPE_COMMAND="cat"
は、osに応じて、ファイルのエンコードと type コマンドを決めている。その他は linux 系しかそうていしない。(笑)
def tmpf():
fname="__mecab_tmp{0}.txt".format(random.randint(4545,46494))
return fname if not os.path.exists(fname) else tmpf()
は一時ファイルを作る。もっとうまく書けそうだが、まあ何でもいい。(笑)
Mecab クラスが肝。以下、関数の意味
init
引数 | 意味 |
---|---|
text | 文字列 |
call
Mecab.__analize__(self.text)
と同じ。
__analize__
返り値 | 意味 |
---|---|
data | 検索結果を一行ごとに返す。 形式は (単語,それ以外のデータのリスト)
|
__getDataAsSentence__
EOSを区切りに、データを文単位で返す。 __analize__ の返り値のリスト。
getDataAsSentence
EOSを区切りにデータを、文にして返す。__getDataAsSentence__の文単位のデータの単語をつなげて返す。
getWords
単語を返す。
__analize__ の中で、わざわざ一時ファイルを作って書き込んでいるのは、大きいデータになると、
for line in sp.check_output("type {0} | mecab".format(tmp),shell=True).decode(ENCODING,"ignore").split("\n"):
の type ...
のところで、メモリーエラーになったことがあるから。もっとうまい方法があるのだろうか。。。
使ってみる
import mecab
import sys
text=sys.argv[1]
for word in mecab.Mecab.__analize__(text):
print(word)
for word in mecab.Mecab.getWords(text):
print(word)
$ python main.py 隣の柿はよく客食う柿だ。
('隣', ['名詞', '一般', '*', '*', '*', '*', '隣', 'トナリ', 'トナリ'])
('の', ['助詞', '連体化', '*', '*', '*', '*', 'の', 'ノ', 'ノ'])
('柿', ['名詞', '一般', '*', '*', '*', '*', '柿', 'カキ', 'カキ'])
('は', ['助詞', '係助詞', '*', '*', '*', '*', 'は', 'ハ', 'ワ'])
('よく', ['副詞', '一般', '*', '*', '*', '*', 'よく', 'ヨク', 'ヨク'])
('客', ['名詞', '一般', '*', '*', '*', '*', '客', 'キャク', 'キャク'])
('食う', ['動詞', '自立', '*', '*', '五段・ワ行促音便', '基本形', '食う', 'クウ', 'クウ'])
('柿', ['名詞', '一般', '*', '*', '*', '*', '柿', 'カキ', 'カキ'])
('だ', ['助動詞', '*', '*', '*', '特殊・ダ', '基本形', 'だ', 'ダ', 'ダ'])
('。', ['記号', '句点', '*', '*', '*', '*', '。', '。', '。'])
('EOS', None)
隣
の
柿
は
よく
客
食う
柿
だ
おわりに
データを返すときに、辞書型にすればもっと使いやすいだろうが面倒やし、個人的な需要がないのでつくらず。必要は発明の母とはよく言ったものだ。(笑)