はじめに
Pyautoguiがあれば大概のRPAが実装できる。周辺モジュールも含めてRPAを実装するための大全をメモします。
この記事の情報でほとんどすべてのGUI操作が可能になるのではないでしょうか?
メインで使用するpyautogui
をpag
としてimportしておく。
import pyautogui as pag
キーボード操作
ユーザーIDやパスワードを有力する際に使用したり、ショートカットキーを送信するために使う。
キーを順番に押す
GUIのボタン操作などでtab連打からのenterを押したいときに便利。
presses
で複数回繰り返すことができる。
下の例ではTab→Enter→Tab→Enter→Tab→Enterと繰り返す。
pag.press(["tab", "enter"], presses=3)
キーを同時押しする
ショートカットキーを送信するときに便利。この例ではctrlを押したままaを押し、aを離したあとにctrlを離す。
pag.hotkey("ctrl", "a")
テキスト入力(英語)
str型のテキストを入力できる。
pag.write("text")
テキスト入力(日本語)
write()
メソッドは日本度を入力できないため、一旦クリップボードを経由させる。
pyperclip
モジュールを用いてクリップボードへアクセスする。
import pyperclip as clip
clip.copy("日本語の文章")
pag.hotkey("ctrl", "v")
キー名称の確認
入力できるキーの一覧は以下のように確認することができる。
print(pag.KEYBOARD_KEYS)
['\t', '\n', '\r', ' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 'accept', 'add', 'alt', 'altleft', 'altright', 'apps', 'backspace', 'browserback', 'browserfavorites', 'browserforward', 'browserhome', 'browserrefresh', 'browsersearch', 'browserstop', 'capslock', 'clear', 'convert', 'ctrl', 'ctrlleft', 'ctrlright', 'decimal', 'del', 'delete', 'divide', 'down', 'end', 'enter', 'esc', 'escape', 'execute', 'f1', 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f2', 'f20', 'f21', 'f22', 'f23', 'f24', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'final', 'fn', 'hanguel', 'hangul', 'hanja', 'help', 'home', 'insert', 'junja', 'kana', 'kanji', 'launchapp1', 'launchapp2', 'launchmail', 'launchmediaselect', 'left', 'modechange', 'multiply', 'nexttrack', 'nonconvert', 'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9', 'numlock', 'pagedown', 'pageup', 'pause', 'pgdn', 'pgup', 'playpause', 'prevtrack', 'print', 'printscreen', 'prntscrn', 'prtsc', 'prtscr', 'return', 'right', 'scrolllock', 'select', 'separator', 'shift', 'shiftleft', 'shiftright', 'sleep', 'space', 'stop', 'subtract', 'tab', 'up', 'volumedown', 'volumemute', 'volumeup', 'win', 'winleft', 'winright', 'yen', 'command', 'option', 'optionleft', 'optionright']
マウス操作
基本的には相対/絶対座標を指定して移動、クリックを行う。
画面内の座標系は以下のようになっている。(公式引用)
0,0 X increases --> +---------------------------+ | | Y increases | | | | 1920 x 1080 screen | | | | V | | | | +---------------------------+ 1919, 1079
マウスを動かす
move(x, y)
で相対座標、moveTo(x, y)
で絶対座標を指定してマウスを動かすことができる。
座標にNone
を指定するとその方向には移動しない。
pag.move(10, 10)
pag.moveTo(100, 100)
クリック
pag.click()
で左クリックを行う。
x, y
を指定すればmove
してからクリックする。
clicks
を指定すれば複数回の連続クリックを実行できる。
button
に"primary"
を指定すればいわゆる左クリック、"secondary"
を指定すればいわゆる右クリック、"middle"
で中クリックを行える。
pag.click(x=100, y=300, clicks=2, button="primary")
ドラッグ
move
と同様に、drag(x, y)
で相対座標、dragTo(x, y)
で絶対座標を指定してマウスを動かすことができる。
座標にNone
を指定するとその方向には移動しない。
pag.drag(10, 10)
pag.dragTo(None, 10)
画面スクロール
scroll
というメソッドが実装されているが、引数の定義が曖昧。
+で上方向、-で下方向に移動する。
pag.scroll(-3000)
テキストエディタなどでは使用しづらいが、場合によっては↓を複数回押したほうが良い場合がある。
pag.press("down", presses=3)
ソフトやアプリケーションの起動
起動系のコマンドは2つ、それぞれメリデメがある。
subprocess.Popen()
subprocess.Popen
メソッドを使用すれば大概のプログラムを起動できる。
また、リスト形式で引数を渡して実行することもできる。
import subprocess
app = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
url = "https://www.google.com/"
subprocess.Popen([app, url])
os.startfile()
os.startfile
メソッドを使用することでsubprocess.Popen
では開けないショートカットを開くことができる。
import os
app = r"C:\Users\Public\Desktop\shortcut.url"
os.startfile(app)
待機する
GUIに対してアクションをおこなった後、何かしら応答が返ってくるまで待ってから次の動作実行しないと予期せぬ暴走を引き起こしかねない。
ここではいくつか待機方法について紹介する。
シンプルに時間で待つ
予め決められた秒数だけ待ってから次の動作を実行する。time.sleep
を使用するか、pyautogui
のsleep
メソッドを使用する。
import time
time.sleep(30)
pag.sleep(30)
画像認識
画像を認識させ、予め準備しておいた画像と一致する部分があったら次の処理を行う。
pics
に認識させたい画像のパスのリストを渡すと、見つかった位置とインデックスを返す。
画像の準備方法も下の方で紹介しています。
どの画像が見つかったかをインデックスで判断できるため、条件分岐の起点として使用できる。
import datetime
import time
def wait_pics(pics, dt=0.0, upto=600.0):
abort_time = datetime.datetime.now() + datetime.timedelta(seconds=upto)
while datetime.datetime.now() < abort_time:
for index, pic in enumerate(pics):
pag.failSafeCheck()
loc = pag.locateCenterOnScreen(pic)
if loc is not None:
return loc, index
time.sleep(dt)
raise TimeoutError()
datetime
モジュールを使用して一定時間過ぎた場合はwhile
文を抜けてTimeoutError
を出力する。
pics
を一つ一つlocateCenterOnScreen
で探す。
見つからなかった場合はNone
が返ってくるため次の画像を探す。
見つかった場合はその座標と画像のインデックスを返す。
使用例は下記の通り。
# test1かtest2の画像がウィンドウに現れるまで待つ
loc, index = wait_pics(["test1.png","test2.png"])
# どちらのインデックスが見つかったか
print(index)
# 見つかった画像の位置へマウスを移動する
pag.moveTo(loc)
ウィンドウタイトルで判断
指定された文字列をタイトルに含むウィンドウが出現するまで待つ。基本的な思想は画像認識待ちと同様。
titles
に検索したいウィンドウ名の文字列リストを渡すと、見つかった位置とインデックスを返す。
import datetime
import time
import pygetwindow as gw
def wait_title(titles, dt=0.0, upto=600.0):
abort_time = datetime.datetime.now() + datetime.timedelta(seconds=upto)
while datetime.datetime.now() < abort_time:
for index, title in enumerate(titles):
pag.failSafeCheck()
win = gw.getWindowsWithTitle(title)
if win != []:
return win[0], index
time.sleep(dt)
raise TimeoutError()
使用例は下記の通り。
# "Outlook"か"Chrome"を含むウィンドウが出現するまで待つ
win, index = wait_title(["Outlook", "Chrome"])
# どちらのインデックスが見つかったか
print(index)
# すべてのウィンドウを最小化
pag.hotkey("win", "d")
# 見つかったウィンドウをアクティベートして最大化
win.activate()
win.maximize()
マウスカーソルの状態で判断
マウスカーソルが砂時計状態ではなくなるまで待機し続ける。
適当な座標を入力するとカーソルを移動し、砂時計状態が終了したらカーソル状態を返す。
import datetime
import time
import win32gui
def wait_hourglass(x=None, y=None, dt=0.0, upto=600.0):
abort_time = datetime.datetime.now() + datetime.timedelta(seconds=upto)
while datetime.datetime.now() < abort_time:
pag.failSafeCheck()
pag.moveTo(x, y)
cursor = win32gui.GetCursorInfo()[1]
if cursor not in [65543, 65561]:
return cursor
time.sleep(dt)
raise TimeoutError()
使用例は下記の通り。
wait_hourglass()
運用上は
画像を認識→マウス移動→ポインター待ち
が安定しそう。
ちなみに私調べでは
通常状態:65539
テキスト入力状態:65541
砂時計とポインター:65561
砂時計:65543
となっていました。このインデックスの調べ方に詳しい方ぜひ教えてください。