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

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

##[Mecabのホームページ](https://taku910.github.io/mecab/)
#コード

```python: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
```

少し複雑にしてしまったが、ご愛敬。

まず

```python
if "nt" in sys.builtin_module_names:
        ENCODING="sjis"
        TYPE_COMMAND="type"
else:
        ENCODING="utf8"
        TYPE_COMMAND="cat"
```

は、osに応じて、ファイルのエンコードと type コマンドを決めている。その他は linux 系しかそうていしない。(笑)

```python
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\_\_ の中で、わざわざ一時ファイルを作って書き込んでいるのは、大きいデータになると、

```python
for line in sp.check_output("type {0} | mecab".format(tmp),shell=True).decode(ENCODING,"ignore").split("\n"):
```

の `type ...` のところで、メモリーエラーになったことがあるから。もっとうまい方法があるのだろうか。。。

#使ってみる

```python: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)
```

```shell:実行結果
$ python main.py 隣の柿はよく客食う柿だ。
('隣', ['名詞', '一般', '*', '*', '*', '*', '隣', 'トナリ', 'トナリ'])
('の', ['助詞', '連体化', '*', '*', '*', '*', 'の', 'ノ', 'ノ'])
('柿', ['名詞', '一般', '*', '*', '*', '*', '柿', 'カキ', 'カキ'])
('は', ['助詞', '係助詞', '*', '*', '*', '*', 'は', 'ハ', 'ワ'])
('よく', ['副詞', '一般', '*', '*', '*', '*', 'よく', 'ヨク', 'ヨク'])
('客', ['名詞', '一般', '*', '*', '*', '*', '客', 'キャク', 'キャク'])
('食う', ['動詞', '自立', '*', '*', '五段・ワ行促音便', '基本形', '食う', 'クウ', 'クウ'])
('柿', ['名詞', '一般', '*', '*', '*', '*', '柿', 'カキ', 'カキ'])
('だ', ['助動詞', '*', '*', '*', '特殊・ダ', '基本形', 'だ', 'ダ', 'ダ'])
('。', ['記号', '句点', '*', '*', '*', '*', '。', '。', '。'])
('EOS', None)
隣
の
柿
は
よく
客
食う
柿
だ
```


#おわりに

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

##[コード](https://github.com/tetsuya111/util/blob/main/mecab.py)