こんにちは、技術研究所の「むらたん」です。
この記事は 『CRESCO Advent Calendar 2017』 3日目の記事です。

 

ロボットを使って業務を自動化するRPA(Robotic Process Automation)が注目されています。
ロボットを使うメリットはあちこちで語られているので詳細は割愛しますが、自動化への期待はIT業界でも高く、苦行のようなテスト実施の自動化はプログラマの夢と希望であったりします。
Webブラウザをスクリプトで操作する「Selenium」というオープンソースのライブラリがあります。ブラウザの起動、指定されたURLの表示、入力値の設定、ボタンアクション、画面イメージの保存など、ブラウザで行う操作をプログラミングして実行できます。スクリプトで記述できることは何でも出来るので、Webシステムのテスト自動化のデファクトスタンダードになりつつあります。
この考え方を流用して、iOSやAndroidのアプリケーションを操作する「Appium」が登場しました。と、歴史を語りだすと止まらなくなるので、そろそろ本題です。

MicrosoftさんのGithub公式アカウントには「Windows Application Driver」が公開されており、Appiumで「Windowsアプリケーション」が操作できます。

!?
これ、Windowsパソコンを操作できるってことは、Webアプリだろうが、Excelだろうが、何でもスクリプトで操作できる最強ツールでではなかろうか。
でも、操作するためのスクリプトを書くのも辛いという方もいらっしゃると思うので、今回は実行環境の構築もプログラミングも簡単な「Python」で行います。
Selenium/Appium、Pythonを知らない方にも実践していただけるよう、細かく説明していきます。

準備するもの

もの 用途 特筆事項 入手先
Windows10のパソコン 諸々の作業環境です。 セットアップ時にはインターネットへの接続が必要になります。 おまかせします
エディタ スクリプトの作成環境です。 今回はVisual Studio Code(VSCode)を利用します。 こちらから
Python スクリプトの実行環境です。 今回は3.6.3を利用します。 こちらから
Windows Application Driver スクリプトからの指示を受けるサーバ機能(以下 ドライバ)です。 こちらから
Windows SDK アプリケーションの解析に使うinspect.exeをインストールします。 スクリプトの実行自体では使用しません。 こちらから

セットアップ

らくらく自動化の手順は以下のとおりです。

ソフトウェアのインストール

  1. 全てのアプリケーションをデフォルト設定のままインストールします。

開発者モードの設定

  1. スタートボタンを押し、歯車ボタンを押して、「Windowsの設定」を開きます。
  2. 更新とセキュリティを押し、左メニューの開発者向けを押します。
  3. 開発者向け機能を使う で「開発者モード」に設定します。

ネットワーク関連設定

必要に応じて、Pythonやスクリプトがドライバに接続できるよう、プロキシやファイアウォールの設定をしてください。

Windowsのプロキシ設定

以下を環境変数に設定します。

項目 説明
http_proxy http://USERNAME:PASSWD@DOMAIN:PORT/ USERNAME:プロキシユーザ名、PASSWD:プロキシユーザパスワード、DOMAIN:プロキシサーバ・ドメイン、PORT:接続ポート番号
no_proxy HOST(,…) HOST:除外ホスト、複数指定はカンマ区切りで指定する

Windowsファイアウォールの設定

紹介する手順はローカルPC上で稼働するドライバに接続する手順のため特別な設定は不要ですが、ローカルPC以外でドライバを使用する時は、外部からの接続を許可してください。

  1. コントロールパネル>システムとセキュリティ>Windowsファイアウォールを選択し、画面左メニューの詳細設定を開く
  1. セキュリティが強化されたWindowsファイアウォールで受信の規則を開く
  1. ポートを選択して、次へ
  1. TCPを選択し、特定のローカルポートで「4723」を指定して、次へ
  1. 接続を許可するを選択して、次へ
  1. プロファイルで、全てチェックして、次へ
  1. 適当な名前を付けて、完了

Python用のAppiumライブラリのインストール

pipコマンドでAppium関連のライブラリを導入するため、コマンドプロンプトで以下を実行します。

pip install robotframework-appiumlibrary

スクリプトの準備

以下のスクリプトはメモ帳でPythonスクリプトを生成後、コマンドプロンプトを起動してスクリプトを実行し、最後にスクリプト自身を消すというものです。
こちらを元にスクリプトの書き方を説明します。ここでは、hello.pyという名前で保存します。

# hello.py
# 説明①
from appium import webdriver
from selenium.webdriver.common.keys import Keys
import time
# 説明②
desired_caps = {}
desired_caps["app"] = "notepad.exe"
# 説明③
driver = webdriver.Remote(
command_executor='http://127.0.0.1:4723',
desired_capabilities= desired_caps)
# 説明④
notepad = driver.find_element_by_class_name('Notepad')
notepad.send_keys('print("Hello Windows Application Driver World!")')
notepad.find_element_by_accessibility_id('MenuBar').send_keys(Keys.ALT + "F")
notepad.find_element_by_accessibility_id('MenuBar').send_keys(Keys.CONTROL + "S")
# 説明⑤
time.sleep(1)
# 説明⑥
notepad.find_element_by_accessibility_id('FileTypeControlHost').click()
notepad.find_element_by_accessibility_id('FileTypeControlHost').send_keys(Keys.DOWN + Keys.ENTER)
notepad.find_element_by_class_name('ComboBox').click()
notepad.find_element_by_class_name('ComboBox').send_keys(Keys.DOWN + Keys.DOWN + Keys.DOWN + Keys.ENTER)
notepad.find_element_by_accessibility_id('1001').send_keys("hello.py")
notepad.find_element_by_accessibility_id('1').click()
# 説明⑦
driver.quit()
# WindowsバッチからPythonスクリプトを実行する
desired_caps = {}
desired_caps["app"] = "cmd.exe"
driver = webdriver.Remote(
command_executor='http://127.0.0.1:4723',
desired_capabilities= desired_caps)
driver.find_element_by_class_name('ConsoleWindowClass').send_keys('python hello.py' + Keys.ENTER)
driver.find_element_by_class_name('ConsoleWindowClass').send_keys('del hello.py' + Keys.ENTER)
# driver.quit()

スクリプトの説明

説明① 必要なライブラリをインポートします。

ここでは、Seleniumでお馴染みのWebDriver、キー操作用のKeys、Wait処理用のtime をインポートしています。

説明② 操作するソフトウェアに関する設定をします。

設定内容をハッシュに格納します。ここでは起動アプリケーション(メモ帳)のみを設定します。パスが通っているので実行ファイルまで指定となっていますが、フルパスを指定することもできます。
他に設定できる項目の詳細はこちらをご覧ください。

説明③ ドライバからアプリケーションを起動します。

command_executorにドライバが起動しているホスト、ポートを指定します。
desired_capabilitiesに②で用意したハッシュを指定します。
このコマンドを実行すると、ドライバがアプリケーションを起動しますので、以降、ドライバに対して指示を出して、アプリケーションを操作します。

説明④、説明⑥ アプリケーションを操作します。

アプリケーションは複数のUIコンポーネントから作られており、「find_element(s)_by~」で操作するロケータを特定し、「send_keys()」でキー操作、「click()」でクリック動作を指示します。
説明④では、メモ帳に「print(“Hello Windows Application Driver World!”)」と入力し、ショートカットで、ファイルの保存を行おうとしています。
説明⑥では、ファイルの保存ダイアログを操作し、実際にファイルを保存します。ダイアログは表示されているときでないと、inspect.exe(後述)で解析することが出来ません。

説明⑤ タイムラグを設定します。

スクリプトの操作が速すぎる時はタイムラグを置きます。
ここでは、ファイルを保存するダイアログの表示を待つため、1秒のウェイト処理を入れています。

説明⑦ ドライバを終了します。

ドライバを終了すると、アプリケーションも終了します。

スクリプト作成時のハマりどころ

ここでは、スクリプトを作成していく中で見つけたハマりどころを紹介します。

ロケータの特定について

スクリプトで指定できるロケータはこちらに書かれています。
ロケータに設定する値はinspect.exeを使って解析します。(インストール先は”C:\Program Files (x86)\Windows Kits\10\bin(バージョン番号)(プロセッサ)\inspect.exe”)

例として、メモ帳で以下のように編集エリアにフォーカスが当たっているとします。

inspect.exeでは、以下のように表示され、編集エリアに関する情報がとれます。

XPathの指定について

上記リンク先のサンプルでは「//Button[0]」のように配列指定をしていますが、上手く取れませんでした。
また、指定する項目は「Any」となっていますが、ControlTypeを加工して指定するようです。
例)メモ帳のウィンドウのControlTypeは「UIA_WindowControlTypeId」ですが、「UIA_」と「ControlTypeId」を取り除いた「Window」が指定項目になります。

send_keyコマンドについて

私はJISキーボードを使っているのですが、send_keyでキーを送る際、キーバインドがUSに変わってしまいます。
send_key(‘\’)とすると、アプリケーションでは「]」が入力されるという挙動になります。回避策が見つかっていません。。。

いざ、実行

ドライバの起動

アプリケーションを操作する環境でWindowAppDriver.exeを起動します。(インストール先は”C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe”)
実行時引数でIPアドレスやポート番号をカスタマイズすることができますが、IPアドレスを変更する場合は、管理者モードで実行する必要があります。

スクリプトの実行

pythonコマンドでスクリプトを実行します。

cd (hello.pyの保存先)
python .\hello.py

最後に

らくらく自動化とは言ったものの、結構手間が掛かります。とはいえ、普段の定型作業がスクリプト化できれば、きっと幸せになれるかと思いますので、ご活用ください。