LoginSignup
12
11

More than 3 years have passed since last update.

pyknp(JUMAN,KNP)をwindowsで使えるようにする

Last updated at Posted at 2020-11-22

目次

1.開発環境
2.各種ソフトをダウンロード
3.コマンドプロンプトで確認
4.pyknpインストール
5.使用テスト
6.プログラム書き換え
7.参考・余談

少しでも参考になると嬉しいです、セットアップ頑張ってください!

1.開発環境

環境
Windows10
*Python-3.7.5 64bit
*
ターミナル?**
*Command prompt
*Visual studio codeのターミナル

2.各種ソフトをダウンロード

まずJUMANとKNPをダウンロードします

1.JUMAN

ダウンロードの欄の上から3番目くらいに、
JUMAN Ver.7.0 (Windows 64bit版) (インストーラ付; 8,330,604 bytes)
ってのがあります。それをダウンロードして、開いたらインストールの作業に移ります
image.png
設定はそのままで大丈夫のはず…

1.KNP

こちらもダウンロードの欄から下のやつ探してダウンロードします
KNP Ver.4.11 (Windows 64bit版) (インストーラ付; 979,363,446 bytes)
なんかサイズが\でけぇ!/のでまあまあ時間かかります
こちらもダウンロードして開いたら、インストールが始まるのでそれに従ってください

PATHを設定する

pathは自分で設定しないといけないです
こちらを参考にさせていただきました。システム側をいじるだけでいけます
1.検索かなんかでシステムのプロパティを開きます
2.詳細設定
3.下の方にある[環境設定]
4.システム環境設定にある[path]
5.[編集]
6.[C:\Program Files\juman]を追加
7.[C: \Program Files\knp]を追加
8.[OK]
人によっては再起動したほうがいいかもしれません。僕は、再起動しないと反映されませんでした

余談:pyknpではJUMAN++とKNPを使うのですが、JUMANをwindowsで使用するにはいろいろ面倒だったので旧バージョンであるJUMANを使用します。これの対応は下の方に書いてあります

3.コマンドプロンプトで確認

まずコマンドプロンプトを開きます(すべてctrl+Cで終了できます)
確認方法1: jumanと入力

C:~\> juman
なんか文章を入力
(成功例)
なんか なんか なんか 副詞 8 * 0 * 0 * 0 "代表表記:なんか/なんか 標準:何/なに+か/か"
文章 ぶんしょう 文章 名詞 6 普通名詞 1 * 0 * 0 "代表表記:文章/ぶんしょう カテゴリ:抽象物"
を を を 助詞 9 格助詞 1 * 0 * 0 NIL
入力 にゅうりょく 入力 名詞 6 サ変名詞 2 * 0 * 0 "代表表記:入力/にゅうりょく カテゴリ:抽象物 ドメイン:科学・技術 反義:名詞-サ変名詞:出力/しゅつりょく"
EOS

確認方法2: echo なんか文章 | juman と入力

C:~\> echo なんか文章を入力 | juman
なんか なんか なんか 副詞 8 * 0 * 0 * 0 "代表表記:なんか/なんか 標準:何/なに+か/か"
文章 ぶんしょう 文章 名詞 6 普通名詞 1 * 0 * 0 "代表表記:文章/ぶんしょう カテゴリ:抽象物"
を を を 助詞 9 格助詞 1 * 0 * 0 NIL
入力 にゅうりょく 入力 名詞 6 サ変名詞 2 * 0 * 0 "代表表記:入力/にゅうりょく カテゴリ:抽象物 ドメイン:科学・技術 反義:名詞-サ変名詞:出力/しゅつりょく"
  \  \  特殊 1 空白 6 * 0 * 0 NIL
EOS

確認方法3: juman | knp と入力

C:\~> juman | knp
なんか文章を入力
# S-ID:1 KNP:4.11-CF1.1 DATE:2020/11/23 SCORE:-27.41598
なんか──┐ 
文章を──┤ 
      入力
EOS

(失敗例)

'juman' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

4.pyknpインストール

VisualStudioCodeのターミナルで作業
pip install pyknpでインストール

ターミナル
C:~\>pip install pyknp

出来なかったら、(僕の場合は文字コードのエラーだったっぽいので)
https://qiita.com/Nidhog-tm/items/c7e9d759ce1a0f5c85c6
に従ってUTF-8を使用
その後おそらくどこかで文字化けが起こるので、チェック外す > 英語にする > 日本語にする、で直す

5.使用テスト

多分ってか絶対エラー吐きます。ここからこのスクリプトを使ってエラーの確認をしていきます
(参考:https://pyknp.readthedocs.io/en/latest/)

test.py
# coding: utf-8
from __future__ import unicode_literals # It is not necessary when you use python3.
from pyknp import Juman
jumanpp = Juman()   # default is JUMAN++: Juman(jumanpp=True). if you use JUMAN, use Juman(jumanpp=False)
result = jumanpp.analysis("下鴨神社の参道は暗かった。")
for mrph in result.mrph_list(): # 各形態素にアクセス
    print("見出し:%s, 読み:%s, 原形:%s, 品詞:%s, 品詞細分類:%s, 活用型:%s, 活用形:%s, 意味情報:%s, 代表表記:%s" \
            % (mrph.midasi, mrph.yomi, mrph.genkei, mrph.hinsi, mrph.bunrui, mrph.katuyou1, mrph.katuyou2, mrph.imis, mrph.repname))

### (成功例) ###
見出し:下鴨, 読み:しもがも, 原形:下鴨, 品詞:名詞, 品詞細分類:地名, 活用型:*, 活用形:*, 意味情報:自動獲得:Wikipedia Wikipedia地名, 代表表記:
見出し:神社, 読み:じんじゃ, 原形:神社, 品詞:名詞, 品詞細分類:普通名詞, 活用型:*, 活用形:*, 意味情報:代表表記:神社/じんじゃ ドメイン:文化芸術 カテゴリ:場所-施設 地名末尾, 代表表記:神社/じんじゃ
見出し:, 読み:, 原形:, 品詞:助詞, 品詞細分類:接続助詞, 活用型:*, 活用形:*, 意味情報:NIL, 代表表記:
見出し:参道, 読み:さんどう, 原形:参道, 品詞:名詞, 品詞細分類:普通名詞, 活用型:*, 活用形:*, 意味情報:代表表記:参道/さんどう ドメイン:文化芸術 カテゴリ:場所-施設, 代表表記:参道/さんどう
見出し:, 読み:, 原形:, 品詞:助詞, 品詞細分類:副助詞, 活用型:*, 活用形:*, 意味情報:NIL, 代表表記:
見出し:暗かった, 読み:くらかった, 原形:暗い, 品詞:形容詞, 品詞細分類:*, 活用型:イ形容詞アウオ段, 活用形:タ形, 意味情報:代表表記:暗い/くらい, 代表表記:暗い/くらい
見出し:, 読み:, 原形:, 品詞:特殊, 品詞細分類:句点, 活用型:*, 活用形:*, 意味情報:NIL, 代表表記:

6.プログラム書き換え

ここがマジ一日かかった。けど原因わかったら数分で行けます…(時間返してもろて)
書き換えるところは、すべてpyknpのファイル内の.pyファイルです。

1.knp.py

knp.py
# (29行目)
# 書き換え内容
jumancommand='jumanpp''juman' にする
jumanpp     = True     False  にする

# 書き換え前
def __init__(self, command='knp', server=None, port=31000, timeout=60,
                 option='-tab', rcfile='', pattern=r'EOS',
                 jumancommand='jumanpp', jumanrcfile='',
                 jumanoption='', jumanpp=True):
# 書き換え後
def __init__(self, command='knp', server=None, port=31000, timeout=60,
                 option='-tab', rcfile='', pattern=r'EOS',
                 jumancommand='juman', jumanrcfile='',
                 jumanoption='', jumanpp=False):

2.juman.py

knp.py
# (27行目)
# 書き換え内容
command = 'jumanpp''juman' にする
jumanpp =  True     False  にする

# 書き換え前
    def __init__(self, command='jumanpp', server=None, port=32000, timeout=30,
                 option='', rcfile='', ignorepattern='',
                 pattern=r'^EOS$', jumanpp=True):
# 書き換え後
    def __init__(self, command='juman', server=None, port=32000, timeout=30,
                 option='', rcfile='', ignorepattern='',
                 pattern=r'^EOS$', jumanpp=False):

3.process.py

process.py
# (72行目)
# 書き換え内容1
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(self.process_timeout)
# ↓
alarm = threading.Timer(self.process_timeout, alarm_handler)
alarm.start()

# 書き換え内容2
self.process.stdin.write(sentence.encode('utf-8') + six.b('\n'))
# ↓ (utf-8をcp932にする)
self.process.stdin.write(sentence.encode('cp932') + six.b('\n'))

# 書き換え内容3
line = self.process.stdout.readline().rstrip().decode('utf-8')
# ↓ (utf-8をcp932にする)
line = self.process.stdout.readline().rstrip().decode('cp932')

# 書き換え内容4
signal.alarm(0)
# ↓
alarm.cancel()


# 書き換え後
    def query(self, sentence, pattern):
        assert(isinstance(sentence, six.text_type))

        def alarm_handler(signum, frame):
            raise subprocess.TimeoutExpired(self.process_command, self.process_timeout)
# 書き換えポイント1
        # signal.signal(signal.SIGALRM, alarm_handler)
        # signal.alarm(self.process_timeout)
        alarm = threading.Timer(self.process_timeout, alarm_handler)
        alarm.start()
        result = ""
        try:
#書き換えポイント2
            # self.process.stdin.write(sentence.encode('utf-8') + six.b('\n'))
            self.process.stdin.write(sentence.encode('cp932') + six.b('\n'))
            self.process.stdin.flush()
            while True:
#書き換えポイント3
                # line = self.process.stdout.readline().rstrip().decode('utf-8')
                line = self.process.stdout.readline().rstrip().decode('cp932')
                if re.search(pattern, line):
                    break
                result = "%s%s\n" % (result, line)
        finally:
#書き換えポイント4
            # signal.alarm(0)
            alarm.cancel()
        return result

ついでに参照の仕組みを解説すると、
knp は juman を参照
(この時使用するコマンド名はjumanpp(JUMAN++)ではなくjumanじゃないとエラー起きる)
juman は process を参照
(この時使用するコマンドもjumanじゃないといけない)
processは subprocess(ターミナルのコマンド実行したりするやつ)を参照
(ここで参照するときに、大半のwindowsは'cp932'という文字コードを使っているせいで、'utf-8'を使うと不具合が起きるからそれを直す。ついでにalarmもwindows用に書き換えてる。subprocess系は、windowsのターミナルであるpowershellを使う用には設計されていないっぽい。面倒ですね。)
という関係になっています

7.参考・余談

参考:
JUMAN >>> http://nlp.ist.i.kyoto-u.ac.jp/index.php?JUMAN
KNP >>> http://nlp.ist.i.kyoto-u.ac.jp/?KNP
pyKNP >>> http://nlp.ist.i.kyoto-u.ac.jp/?PyKNP
参考サイト様:
pyKNPについて >>> https://pyknp.readthedocs.io/en/latest/
subprocessについて >>> https://docs.python.org/ja/3.5/library/asyncio-subprocess.html
記事書いてるときに見つけた良さそうなサイト >>> http://chuckischarles.hatenablog.com/entry/2019/09/12/150505

ここまでに試したこと

最終的に、係り受け解析がしたかったので今回のものに取り組みました

  1. CaboChaという係り受け解析のものがあるのですが、どうやら64bitに対応していないらしく、32bitにするのが面倒なので諦めました。なので今回のKNPを使わせていただいています。(もう一つ必要になる、形態素解析のMeCabってやつはpipとかでおそらく簡単にインストールできます)

  2. pyknpは本来juman++に対応しているが、インストーラーが無く、Linuxでやらないと面倒なことになる(windowsだとpath設定とか環境設定,utf-8などの設定が超つらい。できませんでした。)

  3. python初期搭載のsubprocessというモジュールがあるのですが、これがlinux用に作られていることが大変だったポイントの一つです。例えば、Shell='True'にしないと、windowsのshellが使えないらしいのですが、この設定は危険で推奨されていないっぽいので断念しました。

  4. utf-8で変換すると、上記の仕様のせいで文字列をバイト(?)に変換するときに文字数が減らされるようです。そのため今回は、変換方法をutf-8を使ったものではなく、cp932を使ったものにして対応しました

初投稿なのでミスがあればその都度直していきたいので声をかけてくれるとありがたいです。
この記事が誰かのためになることを祈ります!!!

juman,knp,pyknpを開発した研究室の方々マジでありがとうございます。
この場をお借りして感謝申し上げます。

12
11
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
12
11