ファイル検索したのに、なかなか該当ファイルが見つからないということがある。必死に探し回ってようやく見つけると、検索キーワードの送りがなや表記の揺れが原因でファイル検索で見つからなかったということも多々ある。例えば「引き換え」で検索していたが本文には「引換え」と書かれていた場合だ。今回、こうした表記揺れを吸収するために形態素解析を利用した簡単なファイル検索ツールを作ってみよう。

  • 曖昧検索したところ - grepで見つけられなかったファイルも曖昧検索で見つけることができた

    曖昧検索したところ - grepで見つけられなかったファイルも曖昧検索で見つけることができた

形態素解析で簡単表記揺れ吸収検索

「形態素解析」とは単語辞書を利用して、文章を最小単位の単語に区切る技術だ。多くの形態素解析を行うツールでは、単語辞書を利用して単語のヨミガナを取得する機能がついている。この機能を使う事でちょっとした表記揺れを吸収することができる。

欠点としてはテキストと単語辞書を照合していくため動作速度は遅くなる。それでも、人間が目視でファイルを探し回るよりも確実で早いだろう。また、焦っていると表記揺れが原因でファイルが見つからないというのは、意外と思いつかないものだ。

なお、表記揺れに対応した高度な全文検索ツールもある。そうしたツールでは、送りがなの表記揺れの他、類義語辞書なども利用して検索してくれます。とは言え、わざわざ、そうした専用システムをインストールしなくても、ちょっと検索をかけたいという場面も多い。

そこで、今回はPythonで実装されている形態素解析ライブラリのJanomeだけを利用して、テキストファイルおよびExcel/Wordの検索ツールを作ってみよう。

ライブラリのインストール

それでは、今回利用するライブラリをpipを利用してインストールしてみよう。コマンドライン(WindowsならPowerShell、macOSならターミナル.app)を起動して、以下のコマンドを実行しよう。

$ python3 -m pip install janome openpyxl python-docx

ここでインストールするPythonパッケージは以下の3つだ。

- janome ... 形態素解析を行うライブラリ
- openpyxl ... Excelファイルからテキストを取り出すのに使う
- python-docx ... Wordファイルからテキストを取り出すのに使う

Jaonomeで形態素解析する方法

最初にJaonomeを使って形態素解析を行う方法を紹介しよう。以下のプログラムは「相談によって計画は成功する」という文章を形態素解析するものだ。

import janome.tokenizer
# Janomeを初期化する --- (*1)
tokenizer = janome.tokenizer.Tokenizer() 

# 結果を表示する --- (*2)
s = "相談によって計画は成功する"
for t in tokenizer.tokenize(s):
    print(t)

プログラムを実行する前に、プログラムを見てみよう。最初にJanomeを使うには(*1)のように初期化をしてTokenizerオブジェクトを得る。そして、(*2)のように、tokenizeメソッドを実行すると形態素解析を行う。

上記のプログラムを保存するには「jonome_easy.py」という名前で保存しよう。そしてプログラムを実行するには、PythonのIDLE上で実行するか、コマンドラインで以下のコマンドを実行しよう。

$python3 janome_easy.py

すると、以下のように解析結果が表示される。

  • Janomeを利用して形態素解析を実行したところ

    Janomeを利用して形態素解析を実行したところ

次に、Janomeを利用して、ヨミガナを得る方法を見てみよう。以下のプログラムを「janome_yomi.py」として保存しよう。

import janome.tokenizer
tokenizer = janome.tokenizer.Tokenizer() 
s = "相談によって計画は成功する"
for t in tokenizer.tokenize(s):
    print(t.surface, "(" + t.reading + ")")

プログラムを実行するには、以下のコマンドを実行しよう。

$python3 janome_yomi.py

すると、以下のようにヨミガナが表示される。

  • Janomeを利用してヨミガナを表示したところ

    Janomeを利用してヨミガナを表示したところ

プログラムを見ると分かるとおり、ヨミガナを得るには、tokenizeメソッドで分割した後、t.readingを確認すれば良い。

表記揺れ吸収ファイル検索ツール

それでは、今回作成する表記揺れ検索ファイル検索ツールのプログラムを紹介しよう。以下のプログラムを「search.py」という名前で保存しよう。

import sys, os, unicodedata, docx, openpyxl, janome.tokenizer
tokenizer = janome.tokenizer.Tokenizer() 
def conv_kana(s):
    # 半角全角を統一し正規化してくれる --- (*1)
    s = unicodedata.normalize('NFKC', s)
    # Janomeで全部カタカナに変換 --- (*2)
    r = ''
    for t in tokenizer.tokenize(s):
        r += t.reading if t.reading != '*' else t.surface
    return r

def load_text(filename):
    root, ext = os.path.splitext(filename)
    text = filename + "\n"
    try:
        # テキストファイルを読み込む --- (*3)
        if ext == '.txt' or ext == '.py' or ext == '.md':
            with open(filename, encoding="utf-8") as fp:
                text = fp.read()
        # Wordファイルを読む --- (*4)
        elif ext == '.docx':
            doc = docx.Document(filename)
            for par in doc.paragraphs:
                text += par.text + "\n"
        # Excelファイルを読む --- (*5)
        elif ext == '.xlsx':
            book = openpyxl.load_workbook(filename)
            for sheet in book.worksheets:
                for row in sheet.rows:
                    text += ",".join(
                        [str(cell.value) for cell in row])
                    text += "\n"
    except:
        pass
    return text

def search(path, keyword):
    keyword2 = conv_kana(keyword)
    # ファイル一覧を調べる --- (*6)
    for cur_dir, dirs, files in os.walk(path):
        for file in files:
            # 絶対パスや拡張子などを得る
            target = os.path.join(cur_dir, file)
            text = load_text(target)
            # 普通に検索 --- (*7)
            if keyword in text:
                i = text.find(keyword)
                print("=== [発見]:", target)
                print(">", text[i:i+10].replace("\n",'_'))
                continue
            # 曖昧に検索 --- (*8)
            text2 = conv_kana(text)
            if keyword2 in text2:
                i = text2.find(keyword2)
                print("=== [曖昧]:", target)
                print(">", text2[i:i+10].replace("\n",'_'))

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("[USAGE] search.py keyword path")
        quit()
    search(sys.argv[2], sys.argv[1])

コマンドラインから使うツールで、以下のような書式で利用する。

$ python3 search.py (キーワード) (ファイルパス)

例えば、カレントディレクトリにある「鮭」という文字を持つファイルを探すには以下のように記述する。

$ python3 search.py 鮭 .

すると「サケ」というテキストを持つファイル名と内容が表示される。

=== [曖昧]: ./sample/Book1.xlsx
> サケ,324_カツオ

プログラムを確認してみよう。(*1)の部分では、半角全角を統一するため、unicodedata.normalizeメソッドを実行する。(*2)の部分ではJanomeを利用してテキストを全部ヨミガナに変換する。

(*3)の部分ではテキストファイルを読み込む。(*4)の部分ではWordファイルを読んでテキストデータを取り出す。(*5)ではExcelファイルを読んでテキストファイルとして取り出す。

(*6)の部分ではファイルの一覧を連続で取り出す。(*7)では普通にテキスト検索をして結果を表示する。(*8)では曖昧検索をして結果を表示する。

適当なファイルを配置したら、いろいろ試してみよう。

  • ファイル一覧をいろいろ曖昧検索しているところ

    ファイル一覧をいろいろ曖昧検索しているところ

まとめ

以上、今回は簡単なファイルの曖昧検索のツールを作ってみた。形態素解析を行うことで、日本語の文章を単語に分解し、品詞やヨミガナを得られるので、いろいろな用途に使える。今回は形態素解析を行って、テキストを全部ヨミガナに変換して曖昧検索してみた。

実際にいろいろ試してみると、検索に時間がかかったり、時々ヨミガナが取得できなかったりと改良点がいろいろ見つかった。とは言え、一般的なファイル検索では全く検索できないファイルもうまく見つけることができた。

なお、ここではテキスト形式、Word/Excel形式のファイルだけを検索するプログラムを作ってみたが、自分がよく使うファイル形式を追加したり、もっとうまく検索するようプログラムをさらに改良してみると良いだろう。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。