パワポエンジニアの憂鬱を軽減する誤字/表記揺れ検出ツールを作った物語


# 前書き

Qiita読者の多数を占めるイケてるエンジニア諸氏には無縁の話ではあるが、
世の中には「**パワポエンジニア**」という職種がある。
綺麗なパワポを作ることが彼/彼女らの最大の価値であり、
コードは書かないが、時に「**エクセル方眼紙**」を駆使して、
**"上流工程"**を行う場合もあると聞く。

そんな彼/彼女らの**"品質意識"**は高く、誤字脱字はもちろんのこと、
「ユーザ」「ユーザー」「サーバ」「サーバー」などの表記の揺れや、
「PowerPoint」「powerpoint」「Database」「database」などの
大文字小文字の揺れも許されないようだ。

この記事はそんな彼/彼女らの悩みを
Pythonで解消する風景を描いた物語である。
もちろん**フィクション**であり、
この令和になろうという時代に
**そんなエンジニアは存在しないに決まっている**。

唯一実在する点は、本物語で開発されたツールであり、
以下から無料でダウンロード可能だ。(4/17(水)公開されました)

「**パワポエクセル一発リント君**」（Windows向け）
https://www.vector.co.jp/soft/winnt/business/se519740.html

* **PPTXやXLSXからテキストデータを抽出**
* **完全オフライン ＆ 前提ツール不要で動作**
* **誤字/表記揺れを、複数ファイル横断で検知**

もし伝説の「パワポエンジニア」や「エクセル方眼紙使い」が
実在するとしたら泣いて喜ぶツールだと思われる。

それでは早速、このツールが開発されるにいたった風景を見てみよう。

# パワポエンジニアのお仕事風景より（Before）

ワイ「あー、今日もパワポ、明日もパワポや」
ワイ「最もよく使うツールは**MS PowerPoint**やで」
ワイ「開発したこと無いけど技術に"詳しい感じ"に見せるのが腕の見せ所や」
ワイ「Qiitaのイケてるエンジニアのみんなが羨ましいで～」

上司「お客様にご説明する資料できたか？」
ワイ「はい、できました！」
上司「なんやこれは、誤字だらけやないかい！」

上司「【メーセージ】ってなんや、誤字だろ！」
上司「【アーキテクチャ】【アーキテクチャー】どちらもあるし」
上司「【Kubernetes】【kubernetes】と大文字小文字混ざっとる」
上司「【Kubenetes】って１文字欠けとるのもおるで！」
ワイ「（[k8sについて分かりやすい解説](https://qiita.com/youwht/items/0f8b6c71a799bc8f1d18)を読んでも
　　　まだ間違ってしまうで。スペルが難しいんや！）」

上司「誤字、脱字、表記の揺れ、があると、
　　　**中身もその程度の品質**だって思われるぞ！」
上司「**納品時に厳しくチェックされる**し注意せなあかん！」

ワイ「マコト モウシワケ ゴザイマセン」
上司「[ゲンシジンになっとる](https://qiita.com/youwht/items/6c7712bfc7fd088223a2)で！」

ワイ「・・・。」
ワイ「・・・・・・。」
ワイ「ｚｚｚｚ」
ワイ「はっ！おふとんがなくてもスヤァしそうになるで」
ワイ「誤字/表記揺れチェックはつらみが深いで」
ワイ「でもツマラン誤字脱字で怒られるのもコリゴリや」

3歳娘「パパ、**誤字/表記揺れチェックを自動化して？**」
3歳娘「みんな持ってるの」
3歳娘「私だけ持ってないの」
ワイ「（いや、そのセリフはおねだりの常套句やけど）」
ワイ「（いきなり3歳娘登場は読者がついていけんやろ・・・）」
ワイ「（**アンパンパーソン**あたりから丁寧に導入しないと）」
ワイ「（話の導入部書くのが面倒になったからといっても）」
ワイ「（[やめ太郎さん](https://qiita.com/Yametaro)知ってる人でも驚くほど無理やりやで）」
ワイ「（会話でまとめるの大変であきらめたってバレるで。やめ太郎さんスゴすぎや！）」

ワイ「**さあ、誤字/表記揺れチェックを自動化していくで！**」

◆重要注意事項：
　この物語は**フィクション**です。
　登場する**ワイ**は架空のワイであり、
　実在の誰かや組織とは全く関係ありません。

# パワポとエクセルの誤字/表記揺れを自動チェックするツールが完成！

 * **複数のパワポやエクセルから一括でテキストデータを抽出！**
 * **誤字や表記の揺れを自動機械チェック**
 * **完全オフライン動作** ＆ **チェック規則の作成不要**

以下のページから、**無料**でダウンロード出来ます。

「**パワポエクセル一発リント君**」（Windows向け）
https://www.vector.co.jp/soft/winnt/business/se519740.html


※複数のパワポから一括でテキストを抜き出すツールとしても使えます。
※パワポ以外でも、長文テキストに対する表記揺れチェックとしても使えます。
※もちろん、Qiita投稿時の事前チェックとしてご利用いただくことも可能。
※名前がダサいのは敢えてです。UIも無いような古いツールだし。


# 本投稿の内容

以下ツール開発の流れに沿って
ポイントとなるコード/ノウハウを記載します。

■本ツールの開発の流れ
パワポやエクセルからテキストを抽出
⇒
形態素解析によって名詞を対象とする
⇒
「編集距離」が近い ＝ 似た語句は、誤字/表記揺れの可能性
⇒
Pythonのファイルを「EXE化」して配布可能にする

■主なノウハウ

 * Pythonでどうやってパワポやエクセルを扱うか？
 * 「表記揺れ」をどうやって検知するのか？
 * Pythonを「EXE化」するツールとは？
 * 形態素解析を「EXE化」に組み込むのは大変だったこと
 * 環境は、Windows/Python3.6前提


# **PPTXからテキストを抽出する方法**

これを使う： python-pptx
https://python-pptx.readthedocs.io/en/latest/

以下のコマンドでインストールできる。

```shell-session
pip install python-pptx
```

以下のようにして、テキストを抽出できる。
実行すると、PPTXの中身がダァーっと出てくる。

```py:python-pptxの基本的な使い方
from pptx import Presentation
import sys

try:
    prs = Presentation("input_file_name.pptx")
except:
    ##　パスワード付きなどで開けないとエラーになる。
    ##pptx.exc.PackageNotFoundError: Package not found at  ファイル名
    sys.exit()

#中に入っているslidesの数分の繰り返し
for islide in range(0, len(prs.slides)):
    #スライド名出力　（１階層目）
    print("slide_name=",str(islide))
    #print ("\tfor slide => " + str(islide))
    #python-pptxとしてのスライドを取得
    slide = prs.slides[islide]
    #スライドのshapesの分だけ繰り返す。
    for shape in slide.shapes:
        #テーブル構造を持っている場合は、
        #if shape.has_table:などで分岐して別処理が必要。
        #長くなるのでここでは省略。
        shapeText=""
        #テキスト構造を持たないshapesは無視して次に進む。
        if not shape.has_text_frame:
            continue
        for paragraph in shape.text_frame.paragraphs:
            #.strip()は、改行コードやタブ情報などを削除する。
            shapeText += paragraph.text.strip().replace('\n','').replace('\r','')
        #もし、そのshape内にテキストデータが入っている場合は出力
        if( len(shapeText) >0 ):
            print(shapeText)
```

これで、任意のPowerPointファイル（PPTX）から
テキストデータを抽出できるようになった！

# **XLSXからテキストを抽出する方法**

これを使う： xlrd
https://github.com/python-excel/xlrd
（xlrdは読み込み用で、書き込みたければxlwt）

以下のコマンドでインストールできる。

```shell-session
pip install xlrd
```

以下のようにして、テキストを抽出できる。
実行すると、XLSXの中身がダァーっと出てくる。
（ただしセルに入っている値のみで、オブジェクトは不可。
　どなたか良い方法をご存知であれば教えてください）

```py:xlrdの基本的な使い方
import xlrd

book = xlrd.open_workbook("sample.xlsx")

# ブック内のシート数を取得
num_of_worksheets = book.nsheets
print("シート数",num_of_worksheets)

# 全シートの名前を取得
sheet_names = book.sheet_names()
print(sheet_names)

#ブック内のシート数分繰り返し
for iSheet in range(book.nsheets):
    sheet = book.sheet_by_index(iSheet)
    #行の数だけ繰り返し
    for row_index in range(sheet.nrows):
        #列の数だけ繰り返し
        for col_index in range(sheet.ncols):
            val = sheet.cell_value(rowx=row_index, colx=col_index)
            #emptyとか入る場合もあるので、まずSTRに変換。
            str_val = str(val)
            if len(str_val) >0:
                #print(str_val)
                print(str_val)
```

これで、任意のExcelファイル（XLSX）から
テキストデータを抽出できるようになった！


# **DOCXからテキストを抽出する方法(オマケ追加)**

※初期バージョンでは未実装機能：
　コメントにて要望があったため追加。
　2019/5月上旬までに公開予定。

これを使う： python-docx
https://pypi.org/project/python-docx/

以下のコマンドでインストールできる。

```shell-session
pip install python-docx
```

以下のようにして、テキストを抽出できる。
実行すると、DOCXの中身がダァーっと出てくる。
（通常の文章＋表の中の文章が対象のコード。
　オブジェクトの文章の取得方法は不明。）


```py:python-docxの基本的な使い方
import docx

doc= docx.Document('sample.docx')

#全文章を取得
for par in doc.paragraphs:
    print(par.text)

print("---------------------------")

#全テーブルに対して処理を行う。
for table in doc.tables:
    for row in table.rows:
        #print(row.cells[0].text)
        for cell in row.cells:
            print(cell.text)
```

これで、任意のWordファイル（DOCX）から
テキストデータを抽出できるようになった！

~~ツールの名前は直さなくてもいいかなｗ~~


# **「表記揺れ」を検知する方法**

**レーベンシュタイン距離(Levenshtein distance)**の応用版を使う。

レーベンシュタイン距離とは、
2つの語の間の編集距離のことで、
一方の語句から他方を得るのに必要な「修正作業」の回数のこと。

「修正作業」とは、以下の３つのこと。

* 挿入(insertion)　apple⇒applepen なら３回
* 削除(deletion)　非日常的⇒日常 なら２回
* 置換(alteration)　カンジ⇒カンマ なら１回

これを基本として、誤字チェックのためには、
隣接文字の入れ替えも距離１と考えた方が良い。

* 転置(transposition)　カンジ⇒カジン なら１回

転置も認めたものは、
**Damerau–Levenshtein距離**と呼ばれているそうだ。

さらに、語句の長さも考慮しよう。

PenPinappleApplePen、と
PenPinappleApple、は編集距離＝３になる。
このルールだとPenとbagも編集距離＝３だ。
どう見ても、PenPinappleApplePenの方が似ている。
そこで、
「編集距離」÷「長い方の文字列の長さ」を
**標準化されたDamerau–Levenshtein距離**
とする。必ず０～１の間に入る値になる。

* PenPinappleApplePen　vs　PenPinappleApple ⇒ 0.158
* Pen　vs　bag ⇒ 1.0

２つの異なる語句に対してこの値が一定値以下であれば、
誤字/表記揺れの可能性が高いということ。

原理の説明が長くなった。
実装としてはこれを使う： pylev3
https://pypi.org/project/pylev3/

以下のコマンドでインストールできる。

```shell-session
pip install pylev3
```

「標準化」を加えて、以下のように使おう。

```py:標準化されたDamerau–Levenshtein距離
from pylev3 import Levenshtein
def getNormalizedDamerauLevenshteinDistance(str1,str2):
    maxlength = max([len(str1),len(str2)])
    distance = Levenshtein.damerau(str1, str2)
    return distance/maxlength

print(getNormalizedDamerauLevenshteinDistance('cat', 'ctaa'))
#0.5
print(getNormalizedDamerauLevenshteinDistance('cat', 'cta'))
#0.33333
```

これで、語句同士の編集距離を求められるようになり、
誤字/表記揺れを抽出できるようになった！

~~＃決して、ダジャレ自動生成のために調べていたことが~~
　~~シゴトに役立ちそうと思ったわけじゃないんやで！~~

# **PythonをEXE化する方法**

ツールが出来たあとで「配布」するためには、
pyinstallerを使って「EXE化」したい。

pyinstaller
https://www.pyinstaller.org/

以下のコマンドでインストールできる。

```shell-session
pip install pyinstaller
```

Pythonのファイルを指定して、以下のように使う。

```shell-session
pyinstaller pythonfile.py --onefile --icon=iconfile.ico
```

これで単独で実行できるEXEができる。
ツールの配布先/実行環境のPythonの有無やバージョンに
頭を悩まさずに済むため、大変便利だ。

### pyinstallerのエラー発生時の解決方法：

下記の記事を参考にさせていただき、感謝！！
https://qiita.com/pocket_kyoto/items/80a1ac0e46819d90737f

エラー：「AttributeError: 'str' object has no attribute 'items'」


```shell-session:解決策
pip install --upgrade setuptools
```


エラー：「Cannot find existing PyQt5 plugin directories」


```shell-session:解決策
pip install PyQt5
```



# **形態素解析をPythonのEXE化に組み込む方法**

## 名詞を抽出して「表記揺れ」対象リストを作る

パワポやエクセルから取得したテキストから
名詞を抽出して、レーベンシュタイン距離の比較対象とする。
　（N-gramを使うような方法も有力かもしれない = 検討中）

名詞以外も対象としても良いのだが、
活用の違いなどで誤検知が増えるため名詞に限定した。
さあ、形態素解析ツールを導入しよう。

## Mecabではダメだった

Pythonで形態素解析するツールとしては**Mecab**が有名である。

「赤の他人」の対義語は「白い恋人」 これを自動生成したい物語
https://qiita.com/youwht/items/f21325ff62603e8664e6

の時も**Mecab**を使っており、同様のコードが使えるため、
形態素解析自体の手法の記載はここでは省略する。

だが、ここで大きな問題があった。
Mecabは外部ツール的なポジションであるため、
pyinstallerで作るEXEの中に入ってくれないのだ。
つまり、EXE化は出来るのだが、
**Mecabをインストールしていないパソコンでは**
**動作しないものになってしまう。**


## そこで、Janomeにしてみた

pipでインストール出来るJanomeならば、
pyinstallerでEXEに組み込める、と考えた。

Janome
https://mocobeta.github.io/janome/

以下のコマンドでインストールできる。

```shell-session
pip install janome
```

Mecabの代わりにJanomeを使おう。
ほぼ同様に使える。

## が、JanomeでもEXE化するとエラーが発生！

が、Janomeを含むスクリプトを、
pyinstallerでEXE化後に実行しようとすると、
以下のような**エラーが発生**してしまった。

```shell-session:Janomeを組み込んだEXE実行時のエラーログ
AttributeError: 'Matcher' object has no attribute 'dict_data'
```

今回の開発ではこのエラーが、
情報が少なく最もハマったところだ。

## 解決、そして形態素解析を組み込んだEXEが完成！

どうやらこれは、Janomeで使っている辞書データを。
**pyinstallerが認識してくれず組み込めない**ことが原因。
Janomeは内部的に「sysdic」というパッケージを使っているようだ。
「sysdic」自体はJanomeと同様に「site-packages」フォルダ下にあった。
Janomeのフォルダ下ではないために、
pyinstallerが依存物を引っ張ってこれないという現象だ。

「--onefile」オプションをつけない場合は、
「dist」フォルダ配下（出力されるEXEファイルと同じ場所）に、
「sysdic」をフォルダごとコピーして置けば良いようだが、
「--onefile」オプションは、
単一のEXEファイルに変換するオプションであり、
これが無いと配布物がバラバラしてしまうので、ぜひつけたい。

最終的には「--add-binary」オプションで解決できた。
以下のように書く。

```shell-session:解決策
pyinstaller pythonfile.py --onefile --icon=iconfile.ico --add-binary "C:\Users\YourUserName\Anaconda3\Lib\site-packages\sysdic";sysdic
```

当初「--add-binary」オプションの書き方が良く分かっていなかった。
ファイルパスの後ろの「;sysdic」がポイントで、
EXEファイル側から見た時のパスを書いておける模様。
今回のsysdic以外でも、任意のdllや画像ファイルなどを
使いたい場合は同様に「--add-binary」が有効だ。

なお、EXE化する際には「--key」オプションを入れると、
実行ファイルを暗号化できる。

EXE化したツールは辞書データを含めて非常に重く、
配布物は実に２５０MB前後ある。
このために、Web上で事例がほぼ無かったのだろう。

パワポエンジニアが多い組織(注:架空の組織の話)においては、
Mecabを自分の環境に入れておいてください、とか、
pipでJanomeを入れておいてください、とかは、
**日本語として通じない**ため、
誰でもどこでも使えるように、
重くてもEXEにしたほうが便利であろう。

この方法は、一般の人へツールを作成する時にも使えそうだ。
例えば以前「対義語自動生成ツール」が配布できなかった理由は、
まさにこの形態素解析含みの配布物を作れなかったことが、
最大の要因の一つであるのだから。

# 既存ツールとの特徴比較考察

このような様々なノウハウの組み合わせで、とうとう
「**パワポエクセル一発リント君**」（Windows向け）
https://www.vector.co.jp/soft/winnt/business/se519740.html
（※2019/4/17～公開されました）

は完成した。
単純に（架空のワイが）欲しいものを作るのではなく、
既存ツールと比較して、
それぞれ何か一か所以上は利点があるか確認すべきだ。
具体的なツール名は伏せるが、
本ツールの特徴と、既存ツールとの比較を考えよう。

①**校正ツールの希少性**
文章校正に使えるツール自体がまず少ない。
または高価な専門ツールになってしまう。
その点、本ツールは無料で利用可能だ。

②**パワポ＆エクセルを対象**
文章校正ツールの多くは単純テキストのみを対象にしている。
パワポやエクセルに対応したツールは少ない。
その点、本ツールはパワポやエクセルのテキスト化ツールとしても使える。

③**表記揺れを一括チェック**
Office自体に組み込まれている誤字チェック機能は
「表記の揺れ」には対応できず、
「Kubernetes」のような新語/技術用語にも弱い。
複数のファイルを横断して統一チェックすることも出来ない。
その点、本ツールは自身の資料内で使われている単語と突き合わせるため、
どんな用語だろうが問題なく、しかも複数ファイル横断での一括チェックも可能だ。

④**完全オフライン ＆ 前提ソフト無し**
文章解析要素を含む校正ツールの場合、辞書の包含やAPI利用のために、
Web上に文章を置かないといけない場合が多い。
その点、本ツールは完全にオフライン、ローカル環境で使える。
企業内の資料をチェックするにはほぼ必須となる前提であろう。
しかもインストールの前提ソフトが無く、誰でも使える。
（Officeそのものすら必要ない）

⑤**検出力に優れる**
そこそこの検出力があり、表記揺れの場合に、
どちらに寄せるべきかの候補、使われている回数と共に表示される。
（※検出力については、まだ今後の検証を要するが、
　　人が見つけられなかった多数の誤りを一瞬で見つけたことは事実）
  本ツールのチェック観点は少し独特なので、
  仮に別のツール等を用いている人にも、二重チェックとして役立つであろう。

これだけの特徴があれば、
完全上位互換のツールは世の中に存在しないと考えられる。
この考察をもって、本ツールのリリースを許可することにした。

さあ、新しくなった**ワイ**のお仕事風景を見てみよう。


# パワポエンジニアのお仕事風景より（After）

ワイ「毎日毎日僕らはエクセルの～」
ワイ「マス目揃えて、やになっちゃうよ～」

上司「アホな歌うたってないで」
上司「お客様にご説明する資料できたか？」
上司「あと、設計書もできたか？」
上司「今日は**プレミアムフライデー**だから残業禁止やで」

ワイ「はい、できました！」

上司「へぇ～、できました、ねぇ」
上司「どれどれ」
上司「誤字脱字も申し分ない」
上司「ほう、ここも大文字小文字が統一されて・・・」
上司「おいキミ、こんなパワポどこで！？」

（人さし指を立てて）
ワイ「**パワポエクセル一発リント君**！！」

上司「元ネタの**ビズリーチ**とかけ離れすぎて分からん！！」

ワイ「１０回以上見直したような資料でも」
ワイ「１００ページあると１０種類くらい、何かの誤字や表記揺れが見つかるで」

ワイ「これでワイも**毎日がエブリデイ**やで！」
ワイ「Qiitaへの投稿時に誤字/表記揺れの簡易チェックとしても使えるで！
　　　＃あまり短い文章やコードに向いたツールじゃないけどな」

# おしまい。

「個人開発」は思想（ニーズ）と技術（ノウハウ×努力）の**融合**である。
一個一個の要素は大した話ではない。技術も高度な話はない。
今までの物語もそうだった。

訓練不要で誰でも速読！日本一の速読アプリ「瞬間速読」の個人開発物語（25万DL）
https://qiita.com/youwht/items/a1b7a843888c27490172

【無料】Qiitaの殿堂を作った物語【簡単】
https://qiita.com/youwht/items/9851c2ac9024633fc04e

簡単な要素でもアイデアを組み合わせ、形を創ることは興味深い。

ただ、今回の物語は**気持ちと思想に大いなる矛盾をはらんでいる**。
何が矛盾かはここでは表現できない。
悩み続けることもまた物語なのかもしれない。


# ★投げやりな重要追記事項（あとで消すかも）

Qiitaを見るようなイケてるエンジニア諸氏にとって、
パワポやエクセルの誤字/表記揺れなんて、
誰も気にしていないのは分かっている。
99%の人が不要であるどうでもいい投稿だ。
ツールの公開自体、迷うところがある。~~（実はまだ迷っている）~~

**パワポエンジニアの同志**や**エクセル方眼紙使い**（or 使わされ）がいたら、
「いいね」で公開支持を表現してほしい。

この記事への反応(いいね)が少ない場合、
「良かった。~~病気の子供~~パワポエンジニアはいないんだ....。」（[参考](http://www.kotono8.com/wiki/%E8%89%AF%E3%81%8B%E3%81%A3%E3%81%9F%E3%80%82%E7%97%85%E6%B0%97%E3%81%AE%E5%AD%90%E4%BE%9B%E3%81%AF%E3%81%84%E3%81%AA%E3%81%84%E3%82%93%E3%81%A0%E3%80%82)）
という気持ちで、それはそれで嬉しくなるだろう。
**⇒ Vectorへの公開申請も取り下げるツモリ**
**⇒~~意外にも~~多くのご支持をいただき公開しました**@4/17追記
　先行配布した範囲では、検出力的にはなかなか評判が良かったこともあり。
　ただ、ページ数や対象資料の内容にもよるので、
　ご期待に沿う動きになっていると良いのですが・・・。
　（近日、MS-WORDも対象にするかも？）

今回のツールは用途が楽しく無いので、
いつもに比べて、公開モチベーションが非常に低い・・・。
ちょっと楽しくしようと「ワイ」に登場してもらったが、
かえって闇が深くなってしまった気がする。
**4/17に公開されていないかもしれない。**
or オトナの事情で本記事ごと消滅しているかもしれない。
（Vectorでの公開後に投稿しようかと思ったが、
　Pythonの部分は既に書き終わっているし、
　迷いがある分、時間があると蛇足が増えていくので
　もう投稿しちゃうことにした）

反応(いいね)が多い場合、嬉しみがありながらも、
**日本の未来は大丈夫なのか心配になってしまう。**
各個別のノウハウ記載に対してや、
「ワイ」のネタに対しての、いいねだと思うことにする。
（そして気が向いたらmac版もコンパイルしておく）

ところで、こんな話を思い出した。
痩身エステや痩身サプリの主なターゲット顧客は、
超太っている人ではなく、むしろ標準に近い人、と聞いたことがある。
**そのサービスの効果を最大に得ることができそうな人は、**
**そのサービスを全く受ける気が無い≒課題を気にしていない**、という。
パワポエンジニアはQiitaを見ないし、
チェックを自動化するツールを探したりもしない、ような気がする。

---

この物語はフィクションです。
登場する人物・団体・名称等は架空であり、
実在のものとは関係ありません。
「ワイ」「上司」のモデルとなった人・組織はありません。
※ ツールが公開 or 申請取り消し、した際には、
　ダウンロードURL表示のところは直しておきます。