0
1

More than 1 year has passed since last update.

PythonからMecab (少しだけ使いやすく)

Last updated at Posted at 2021-10-26

はじめに

Mecabとは形態素解析エンジンで、要するに文章を解析するソフトである。(僕が言うまでもないか。) googleやyahooにも使われてるらしく、昨日は素晴らしいの一言である。そして今回は、mecabをpythonから使いやすくしたい。python-mecab というライブラリがあって調べると重要なもののようだが、素人にはよくわからない。使いやすくなってるようには見えないので一応書いてみる。

Mecabのホームページ

コード

mecab.py
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 ... のところで、メモリーエラーになったことがあるから。もっとうまい方法があるのだろうか。。。

使ってみる

main.py
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)
隣
の
柿
は
よく
客
食う
柿
だ

おわりに

データを返すときに、辞書型にすればもっと使いやすいだろうが面倒やし、個人的な需要がないのでつくらず。必要は発明の母とはよく言ったものだ。(笑)

コード

0
1
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
0
1