筆者は総務経理部でプログラマーではないですが、2ヶ月ほど本を読むなりしてPythonをかじってみました。
その一番の動機は、「RPA」チックなことができそうだと思ったためです。
なぜそう思ったかというえば、GUI入力しか持たずに入力が非効率極まりない会社の伝票発行システムがあり、それをターゲットにしたかったのです。
そのシステムはGUIのウィンドウにフォーカスさえ当たっていればキーボードのみで入力が完結するので、RPA向きです。
ゴール設定と処理の流れ
ゴールは次のように設定しました。
過去入力した入出金・振替等の経理伝票を再利用し、変更箇所だけ手直ししてあとは自動入力できるようにする
処理とデータの流れは下の図のイメージです。
過去のデータはMySQLのDBに貯まるようになっていたので、そこから検索して取り出します。
取り出したデータを一覧化して取捨選択したり値を加工するのにはやはりExcelが皆慣れているので、SQLクエリの発行とデータ取込はExcelにてVBAに任せることにします。
入力データが決まったら、ExcelからVBAでPythonを実行して、業務システムのウィンドウにキー入力を始めます。ここが今回の肝です。
処理の詳細
具体的な処理について、順に沿っておおまかに説明していきます。
MySQL DB → Excel
WindowsにODBCドライバーをインストールしてその機能を使いました。たまたま別のツールでODBCを全PCに入れていたのでそれがネックにはなりませんでした。
ODBCドライバーは32bit/64bitがあり、更にExcelも64bitOSであっても32bit版が動いてたりもするので、そのあたりをしっかり合わせこまないと動きません。ODBCのインストール方法についてはたくさん情報がありますので省略。
ExcelのVBAからODBCを使うやり方は、「参照設定」か「CreateObject関数を使う」という2つのやり方があります。
そもそもこれらの方法自体今や古そうなのですが、「MySQL DB → Excel」の部分は3年前に先に作ったものなので、できあいのものを使いまわしました。
今となってはこれもPythonでやったほうが良いかもしれませんが、あまりPCを使わない人でもExcelだけは慣れているので、VBAでやる価値はそこにあります。
参考にするなら下記のサイトが良いかと思います。
Excel → Python
これからが本題の、ExcelからPythonコードを実行させる方法ですが、xlwingsというモジュールを使います。
アドインから実行する方法と、VBAの標準モジュールに直接インポートする方法がありますが、あくまでVBAチックにPythonを実行したかったので、アドインのUIを使わない後者にしました。次のサイトがわかりやすいです。
VBAからのPython呼び出し方も、上のサイトのまんま!
Python → キー自動入力
Pythonによるキーボード入力のエミュレートはpyautoguiを使います。
これも情報が大量にあって参考サイトを探すのがたいへんですが、下記がシンプルですね。
その他の細かいこと
なんだやってることはコピペじゃないか、という声があったとしたら、それはPythonの素晴らしさを言い当てています。私なんかの非プログラマーでもネットで調べたらそれなりにできるわけですから。
しかし「やっぱりコピペだけでは動かない」というのもまた理。
最終的に使えるものにするに当たって、細かいノウハウを繋ぎ込んでいきましたので紹介します。
入力したいアプリウィンドウを最前面にする
ExcelからPythonでキー入力を始める前に、当然ですが業務アプリのウィンドウを最前面にしておかないといけません。そうでないとExcelのセルに入力が始まってしまいます。
ということでこれまたいろいろなサイトを参考にするも、かなり苦戦しました。もはや拝借したコードをどのサイトから持ってきたか忘れたくらいです。
簡潔明瞭な方法がなかなか見つからず、pyautoguiでAlt+Tab入力するしか無いかとあきらめかけたところ、win32guiにて実現できました。
1 2 3 4 5 6 7 8 9 10 |
# ターゲットウィンドウを最前面化 import win32gui hWnd2 = win32gui.FindWindow(‘業務アプリのウィンドウクラス名’,’Webjet(F6970)') if hWnd2 != 0: win32gui.SetWindowPos(hWnd2, win32con.HWND_TOPMOST, 0, 0, 0, 0,win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) win32gui.SetWindowPos(hWnd2, win32con.HWND_NOTOPMOST, 0, 0, 0, 0,win32con.SWP_SHOWWINDOW | win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) |
FindWindow()で業務アプリのウィンドウハンドルを取得し、win32gui.SetWindowPos()で常に手前にセットした後、それを解除する、という処理です。
ただしそれだけだと、最前面に来てもアクティブにはならずキー入力を受け付けないということで、今度はwin32apiでマウスエミュレートしてウィンドウの無難な場所をクリックすることで、ウィンドウをアクティブ化します。
1 2 3 4 5 6 7 8 9 10 |
# ウィンドウの左上の隅っこをクリックしてアクティブにする。 import win32api (left, top, right, bottom) = win32gui.GetWindowRect(hWnd2) win32api.SetCursorPos((left + 1, top + 1)) sleep(0.1) win32api.mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0) sleep(0.1) win32api.mouse_event(MOUSEEVENTF_LEFTUP, 0, 0) |
適宜sleepを入れてタイミングを調整しています。pyautoguiでもできるかもしれませんが、ネットの知者に右ならえしました。
pyautoguiで日本語入力
pyautoguiでは日本語入力ができません。
つまり
pyautogui.typewrite(“日本語”)
としてもできないのです。
回避方法としては、コピペならできるということで、日本語文字列をコピーした上で、ctrl + vで入力します。
そこで問題が。なんと、我が社のアンティーク業務アプリは、ctrl + vができなかったのです。
それはそれは絶望しましたが、これまでの努力を水の泡にはできない。ということで、超強引に、1度の変換キー押下で狙った日本語に変換されるよう、辞書ツールを弄る方法を編み出しました。次の流れです。
- MSIMEの辞書ツールを起動
- PowerShellでユーザー辞書のパスをダミー辞書と差し替える
- ダミー辞書に対して、変換したい日本語が変換キー一発で出るような単語登録する
- 日本語自動入力(かな入力+変換キー1回)
- 入力が終わったらダミー辞書を削除し、元のユーザー辞書のパスに戻す
はい、何やってるんだ俺は感が果てしないです。コードにすると下記です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# Pythonからpowershellを実行してユーザー辞書の場所を変更 import os import shutil #ファイルコピーで使用 import datetime from time import sleep orgDicPath = (ユーザー辞書のファイルパス文字列) #ダミーとなるユーザー辞書のテンプレート src = r'C:\temp\tempDic.dic' #ダミー辞書ファイル名を時間から生成し、テンプレート辞書をその名前で複製 now = datetime.datetime.now() copy = 'C:\\temp\\' + now.strftime('%Y%m%d_%H%M%S') + '.dic' shutil.copyfile(src, copy) #ユーザー辞書のパスをダミー辞書に変更 power_commond = r'"C:\Windows\System32\IME\IMEJP\IMJPUEXC.exe" SETCUSTOMDICTPATH ' + '"' + copy + '"'os.system('powershell -Command' + ' ' + power_commond) # 入力したい単語の辞書ファイル作成 inPath = r'c:\temp\importDic.txt' if not os.path.isfile(inPath): print("ファイルパス不明: " + inPath) sys.exit() with open(inPath, "w", encoding="shift_jis") as f: <単語登録用文字列生成処理(読み・単語・品詞をタブ区切り)> f.write(outStr) # ユーザー辞書登録ツール起動 os.system('powershell -Command' + ' ' + r'C:\Windows\System32\IME\IMEJP\IMJPDCT.EXE /t') sleep(2) # 辞書登録ツールを前面に hWnd = win32gui.FindWindow("msime_DictTool", None) if hWnd is not 0: win32gui.SetForegroundWindow(hWnd) # 単語登録する pyautogui.hotkey('alt', 't') sleep(0.5) pyautogui.press('t') sleep(1) pyautogui.typewrite(inPath) #辞書指定 pyautogui.press('enter') sleep(1.5) pyautogui.press('enter') sleep(0.5) pyautogui.hotkey('alt', 'f4') #閉じる <pyautoguiを使った自動キー入力処理> # 一時的な辞書を削除 os.remove(copy) os.remove(copy + '_bak') # Pythonからpowershellを実行してユーザー辞書の場所をもとに戻す power_commond = '"C:\Windows\System32\IME\IMEJP\IMJPUEXC.exe" SETCUSTOMDICTPATH ' + orgDicPath os.system('powershell -Command' + ' ' + power_commond) |
なんとか強引にできたものの、適宜スリープを入れるので辞書登録に10秒くらいかかるし、いくらなんでも美しくない。
藁にもすがる思いで、ホコリを被った業務システムの技術資料をひっくり返して見たところ、なんだかコピペ機能について記述された箇所を発見。
僅かな望みをもって、そのアンティークシステムの見守り人、つまり業務システムの保守メーカーに「コピペできそうだけどなぜできない?」と聞いてみました。
そうしたら、「設定すればできるかも」と。
え!?なぜできない設定になってるの?中小企業社員はコピペ操作を知らないとでも?
結果、コピペができるようシステム設定してもらいました。
このシステム使って15年以上経ち、あと3年くらいしか使うつもりがないのに、今更、「入力でコピペができる!」という大幅パワーアップを遂げたのです。
いや、今まで誰も疑問に思わない社員も社員だし、無駄に機能を外しているSEもSEだし。
これにてめでたく、「ユーザー辞書登録をPythonで自動化する」というアクロバティックな実装は、無駄となりました。
弔いの意味を込めてここで公開しました。
Escapeキーでpyautoguiを強制終了する
pyautoguiでGUIの自動入力するのは良いものの、思わぬ結果のまま入力が突き進んだらたいへんなことになります。
そこでフェールセーフ設計として、手動でマウスカーソルを座標(0,0)に持っていくと動作がとまる仕掛けを有効にしておきます。
pyautoguiでコロン「:」が入力できないバグを直す
このようなバグがありますので、ライブラリのpyファイルを直接直します。
GitHubにもレコードが上がっていますが、やり方は下のサイトをご覧ください。
疲れた!だが動いて爽快
このような苦労の末、なんとかやりたかったことが実現できました。
エクセルで作った使い回しのデータが、業務システムに勝手に入力されていく様は、感動的です。
こんな便利なものを、オール無料で作れてしまうことが素晴らしい。
ただし、私みたいな好きモノの苦労が裏では必要となります。
今回の記事が誰かの一助になることを願ってやみません。