Python
Excel
自然言語処理
PowerPoint
PyInstaller

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


前書き

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】って1文字欠けとるのもおるで!」

ワイ「(k8sについて分かりやすい解説を読んでも

   まだ間違ってしまうで。スペルが難しいんや!)」

上司「誤字、脱字、表記の揺れ、があると、

   中身もその程度の品質だって思われるぞ!」

上司「納品時に厳しくチェックされるし注意せなあかん!」

ワイ「マコト モウシワケ ゴザイマセン」

上司「ゲンシジンになっとるで!」

ワイ「・・・。」

ワイ「・・・・・・。」

ワイ「zzzz」

ワイ「はっ!おふとんがなくてもスヤァしそうになるで」

ワイ「誤字/表記揺れチェックはつらみが深いで」

ワイ「でもツマラン誤字脱字で怒られるのもコリゴリや」

3歳娘「パパ、誤字/表記揺れチェックを自動化して?

3歳娘「みんな持ってるの」

3歳娘「私だけ持ってないの」

ワイ「(いや、そのセリフはおねだりの常套句やけど)」

ワイ「(いきなり3歳娘登場は読者がついていけんやろ・・・)」

ワイ「(アンパンパーソンあたりから丁寧に導入しないと)」

ワイ「(話の導入部書くのが面倒になったからといっても)」

ワイ「(やめ太郎さん知ってる人でも驚くほど無理やりやで)」

ワイ「(会話でまとめるの大変であきらめたってバレるで。やめ太郎さんスゴすぎや!)」

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

◆重要注意事項:

 この物語はフィクションです。

 登場するワイは架空のワイであり、

 実在の誰かや組織とは全く関係ありません。


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


  • 複数のパワポやエクセルから一括でテキストデータを抽出!

  • 誤字や表記の揺れを自動機械チェック


  • 完全オフライン動作チェック規則の作成不要

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

パワポエクセル一発リント君」(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/

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

pip install python-pptx

以下のようにして、テキストを抽出できる。

実行すると、PPTXの中身がダァーっと出てくる。


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)):
#スライド名出力 (1階層目)
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)

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

pip install xlrd

以下のようにして、テキストを抽出できる。

実行すると、XLSXの中身がダァーっと出てくる。

(ただしセルに入っている値のみで、オブジェクトは不可。

 どなたか良い方法をご存知であれば教えてください)


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/

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

pip install python-docx

以下のようにして、テキストを抽出できる。

実行すると、DOCXの中身がダァーっと出てくる。

(通常の文章+表の中の文章が対象のコード。

 オブジェクトの文章の取得方法は不明。)


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)から

テキストデータを抽出できるようになった!

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


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

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

レーベンシュタイン距離とは、

2つの語の間の編集距離のことで、

一方の語句から他方を得るのに必要な「修正作業」の回数のこと。

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


  • 挿入(insertion) apple⇒applepen なら3回

  • 削除(deletion) 非日常的⇒日常 なら2回

  • 置換(alteration) カンジ⇒カンマ なら1回

これを基本として、誤字チェックのためには、

隣接文字の入れ替えも距離1と考えた方が良い。


  • 転置(transposition) カンジ⇒カジン なら1回

転置も認めたものは、

Damerau–Levenshtein距離と呼ばれているそうだ。

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

PenPinappleApplePen、と

PenPinappleApple、は編集距離=3になる。

このルールだとPenとbagも編集距離=3だ。

どう見ても、PenPinappleApplePenの方が似ている。

そこで、

「編集距離」÷「長い方の文字列の長さ」を

標準化されたDamerau–Levenshtein距離

とする。必ず0~1の間に入る値になる。


  • PenPinappleApplePen vs PenPinappleApple ⇒ 0.158

  • Pen vs bag ⇒ 1.0

2つの異なる語句に対してこの値が一定値以下であれば、

誤字/表記揺れの可能性が高いということ。

原理の説明が長くなった。

実装としてはこれを使う: pylev3

https://pypi.org/project/pylev3/

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

pip install pylev3

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


標準化された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/

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

pip install pyinstaller

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

pyinstaller pythonfile.py --onefile --icon=iconfile.ico

これで単独で実行できるEXEができる。

ツールの配布先/実行環境のPythonの有無やバージョンに

頭を悩まさずに済むため、大変便利だ。


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

下記の記事を参考にさせていただき、感謝!!

https://qiita.com/pocket_kyoto/items/80a1ac0e46819d90737f

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


解決策

pip install --upgrade setuptools


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


解決策

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/

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

pip install janome

Mecabの代わりにJanomeを使おう。

ほぼ同様に使える。


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

が、Janomeを含むスクリプトを、

pyinstallerでEXE化後に実行しようとすると、

以下のようなエラーが発生してしまった。


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」オプションで解決できた。

以下のように書く。


解決策

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化したツールは辞書データを含めて非常に重く、

配布物は実に250MB前後ある。

このために、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)

ワイ「毎日毎日僕らはエクセルの~」

ワイ「マス目揃えて、やになっちゃうよ~」

上司「アホな歌うたってないで」

上司「お客様にご説明する資料できたか?」

上司「あと、設計書もできたか?」

上司「今日はプレミアムフライデーだから残業禁止やで」

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

上司「へぇ~、できました、ねぇ」

上司「どれどれ」

上司「誤字脱字も申し分ない」

上司「ほう、ここも大文字小文字が統一されて・・・」

上司「おいキミ、こんなパワポどこで!?」

(人さし指を立てて)

ワイ「パワポエクセル一発リント君!!」

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

ワイ「10回以上見直したような資料でも」

ワイ「100ページあると10種類くらい、何かの誤字や表記揺れが見つかるで」

ワイ「これでワイも毎日がエブリデイやで!」

ワイ「Qiitaへの投稿時に誤字/表記揺れの簡易チェックとしても使えるで!

   #あまり短い文章やコードに向いたツールじゃないけどな」


おしまい。

「個人開発」は思想(ニーズ)と技術(ノウハウ×努力)の融合である。

一個一個の要素は大した話ではない。技術も高度な話はない。

今までの物語もそうだった。

訓練不要で誰でも速読!日本一の速読アプリ「瞬間速読」の個人開発物語(25万DL)

https://qiita.com/youwht/items/a1b7a843888c27490172

【無料】Qiitaの殿堂を作った物語【簡単】

https://qiita.com/youwht/items/9851c2ac9024633fc04e

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

ただ、今回の物語は気持ちと思想に大いなる矛盾をはらんでいる

何が矛盾かはここでは表現できない。

悩み続けることもまた物語なのかもしれない。


★投げやりな重要追記事項(あとで消すかも)

Qiitaを見るようなイケてるエンジニア諸氏にとって、

パワポやエクセルの誤字/表記揺れなんて、

誰も気にしていないのは分かっている。

99%の人が不要であるどうでもいい投稿だ。

ツールの公開自体、迷うところがある。(実はまだ迷っている)

パワポエンジニアの同志エクセル方眼紙使い(or 使わされ)がいたら、

「いいね」で公開支持を表現してほしい。

この記事への反応(いいね)が少ない場合、

「良かった。病気の子供パワポエンジニアはいないんだ....。」(参考

という気持ちで、それはそれで嬉しくなるだろう。

⇒ Vectorへの公開申請も取り下げるツモリ

意外にも多くのご支持をいただき公開しました@4/17追記

 先行配布した範囲では、検出力的にはなかなか評判が良かったこともあり。

 ただ、ページ数や対象資料の内容にもよるので、

 ご期待に沿う動きになっていると良いのですが・・・。

 (近日、MS-WORDも対象にするかも?)

今回のツールは用途が楽しく無いので、

いつもに比べて、公開モチベーションが非常に低い・・・。

ちょっと楽しくしようと「ワイ」に登場してもらったが、

かえって闇が深くなってしまった気がする。

4/17に公開されていないかもしれない。

or オトナの事情で本記事ごと消滅しているかもしれない。

(Vectorでの公開後に投稿しようかと思ったが、

 Pythonの部分は既に書き終わっているし、

 迷いがある分、時間があると蛇足が増えていくので

 もう投稿しちゃうことにした)

反応(いいね)が多い場合、嬉しみがありながらも、

日本の未来は大丈夫なのか心配になってしまう。

各個別のノウハウ記載に対してや、

「ワイ」のネタに対しての、いいねだと思うことにする。

(そして気が向いたらmac版もコンパイルしておく)

ところで、こんな話を思い出した。

痩身エステや痩身サプリの主なターゲット顧客は、

超太っている人ではなく、むしろ標準に近い人、と聞いたことがある。

そのサービスの効果を最大に得ることができそうな人は、

そのサービスを全く受ける気が無い≒課題を気にしていない、という。

パワポエンジニアはQiitaを見ないし、

チェックを自動化するツールを探したりもしない、ような気がする。


この物語はフィクションです。

登場する人物・団体・名称等は架空であり、

実在のものとは関係ありません。

「ワイ」「上司」のモデルとなった人・組織はありません。

※ ツールが公開 or 申請取り消し、した際には、

 ダウンロードURL表示のところは直しておきます。