Python Windows環境下でのRPA「pyautogui」

'21/08/15更新:RPAで操作対象アプリを最前面へアクティブ化する関数を追加

 本記事では、Windows環境下で、Pythonとそのライブラリ「pyautogui」を用いてRPA(Robotic Process Automation)する例として、メモ帳とエクセルを操作する雛形コードを載せました。

ソフトウェアの起動には、subprocess.Popen()を使用します。ウィンドウのアクティブ化にはwin32guiを使います。作成したファイルの保存と終了はショートカットキーによるメニューバー操作で自動化を実現します。

 下図はその実施例です。メモ帳に一万個の💩を生成します。(ちなみに、この中にひとつだけ山があります。ウォーリーを探せ的なうんこ中の山を探せ!)

f:id:HK29:20210814190708p:plain

下図はエクセルで実行した例です。階段状に斜めに💩を大量生成しています。

f:id:HK29:20210814191736p:plain

💩生成過程は、下記動画をご覧ください。

www.youtube.com

 

■本プログラム

Linux環境下での雛形コードは次のリンク先を参照下さい→Python Linux環境下でのRPA「pyautogui」 - PythonとVBAで世の中を便利にする

import os
import pyautogui as ag
import subprocess as sp
import win32gui
import win32con
import time
import random
import datetime
now = datetime.datetime.now()
now = now.strftime("%y%m%d_%H%M%S")


# ウィンドウをアクティブ化する関数
def active_window_func(hwnd, title):
    name = win32gui.GetWindowText(hwnd)
    print(name)
    if name.find(title) >= 0:
        # 最小化を戻す
        if win32gui.IsIconic(hwnd):
            win32gui.ShowWindow(hwnd,1)

        myapp = win32gui.FindWindow(None, name)
        win32gui.SetWindowPos(myapp, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
        left, top, right, bottom = win32gui.GetWindowRect(myapp)
        time.sleep(1)
        center = int((left + right)/2)
        ag.moveTo(center, top + 10)
        ag.click()
        
        return # 関数の終了
    

# メモ帳を起動して文字を書いてファイルに保存する関数
def memo_func():
    # 保存するファイル名
    file_name = now + '.txt'
    # 保存するファイルパス
    save_file_path = dir_path + r'/' + file_name
    # 使用OS用に変換
    save_file_path = save_file_path.replace('/', os.sep)

    # メモ帳をpopenで起動
    sp.Popen('notepad.exe')
    time.sleep(1) # 1秒間待つ
    
    # ウィンドウをアクティブにする
    win32gui.EnumWindows(active_window_func, 'メモ帳')
    
    ag.typewrite('Hello') # 文字をタイプする
    ag.press('space') # スペース
    ag.press('kanji') # 日本語に変換
    ag.typewrite('memotyou !!') # 「メモ帳 !!」と書く
    ag.press('enter') # 決定

    time.sleep(2) # 2秒待つ
    ag.hotkey('ctrl', 'a') # 全選択
    ag.press('del') # 消す
    
    # 作業1. 💩マークを作成
    ag.typewrite('unnko')
    for _ in range(1):
        ag.press('space') # スペース(💩マークに変換するため)
    ag.press('enter') # 決定

    # 作業2. 💩マーク1個をコピーして、1回貼り付け(2個にする)
    ag.hotkey('shift', 'left') # シフト押しながら左
    ag.hotkey('ctrl', 'c') # コピー
    ag.press('right') # 全選択を外す
    ag.hotkey('ctrl', 'v') # ペースト
    
    # 作業3. 💩マーク2個をコピーして、4回貼り付け(10個にする)
    ag.keyDown('shift') # シフト押したまま
    ag.press('left')
    ag.press('left')
    ag.keyUp('shift') # シフトキーを離す
    ag.hotkey('ctrl', 'c') # コピー
    ag.press('right') # 全選択を外す
    for _ in range(4):
        ag.hotkey( "ctrl", "v")

    # 作業4. 💩マーク10個をコピーして、1回貼り付け(20個にする)
    ag.keyDown('shift') # シフト押したまま
    ag.press('home')
    ag.keyUp('shift') # シフトキーを離す
    ag.hotkey('ctrl', 'c') # コピー
    ag.press('right') # 全選択を外す
    ag.hotkey('ctrl', 'v') # ペースト

    # 作業5. 💩マーク20個をコピーして、4回貼り付け(100個にする)
    ag.keyDown('shift') # シフト押したまま
    ag.press('home')
    ag.keyUp('shift') # シフトキーを離す
    ag.hotkey('ctrl', 'c') # コピー
    ag.press('right') # 全選択を外す
    for _ in range(4):
        ag.press('enter') # 改行する
        ag.hotkey('ctrl', 'v') # ペースト

    # 作業6. 💩マーク100個を9回コピペする(1000個にする)
    ag.hotkey( "ctrl", "a") # 全選択
    ag.hotkey('ctrl', 'c') # コピー
    ag.press('right') # 全選択を外す
    for _ in range(9):
        ag.press('enter') # 改行
        ag.hotkey('ctrl', 'v') # ペースト

    # 作業7. 💩マーク1000個を9回コピペする(1万個にする)
    ag.hotkey( "ctrl", "a") # 全選択
    ag.hotkey('ctrl', 'c') # コピー
    ag.press('right') # 全選択を外す
    for _ in range(9):
        ag.press('enter') # 改行
        ag.hotkey('ctrl', 'v') # ペースト
        
    # 作業8. カーソルを乱数で移動する
    row_up = random.randint(0, 500)
    col_left = random.randint(0, 20)
    print(r'(up, left) = ', row_up, col_left)
    for _ in range(row_up):
        ag.press('up')
    for _ in range(col_left):
        ag.press('left')

    # 作業9. ⛰マークを挿入する
    ag.typewrite('yama')
    for _ in range(1):
        ag.press('space') # スペース(⛰マークに変換するため)
    ag.press('enter') # 決定
    ag.press('del') # 💩マークをひとつ消す
    
    # 文字入力を英数字に戻す
    ag.press('kanji')

    # ファイルに保存する
    ag.hotkey('alt', 'f') # メニューバーからファイルを選択
    ag.hotkey('ctrl', 'shift', 's') # ファイル保存のダイアログを開く

    # 保存するファイルパスを書く
    ag.typewrite(save_file_path)
    
    # ファイルを保存する
    ag.press('enter')
    time.sleep(1)

    # メモ帳を閉じる
    ag.hotkey('alt', 'f') # メニューバーからファイルを選択
    ag.press('x')
    print(save_file_path)    


# エクセルを起動してセルを操作してファイルに保存する関数
def excel_func():
    # 保存するファイル名
    file_name = now + '.xlsx'
    # 保存するファイルパス
    save_file_path = dir_path + r'/' + file_name
    # 使用OS用に変換
    save_file_path = save_file_path.replace('/', os.sep)

    # アプリを開く。Popenにより、アプリを閉じるのを待たずに次のスクリプトへ進む
    sp.Popen(r'C:\Program Files\Microsoft Office 15\root\office15\EXCEL.EXE')
    time.sleep(6) # 6秒待つ

    # 新規ブックを起動
    ag.press('enter')

    # ウィンドウをアクティブ化
    win32gui.EnumWindows(active_window_func, 'Excel')

    # 文字をタイプする
    ag.typewrite('Hello Excel !!')
    ag.press('enter')
    # 連続でボタンを押したい場合ではリストで与えることもできる
    str_list = ['enter', 'down', 'tab']
    ag.press(str_list)
    
    # セルに入力
    ag.press('kanji') # 日本語に変換
    ag.typewrite('unnko')
    for _ in range(1):
        ag.press('space') # スペース(💩マークに変換するため)
    ag.press('enter') # 決定
    ag.press('down') # 
    ag.press('up') # 
    ag.hotkey('ctrl', 'c') # セルをコピー
    
    # 同じボタンを連続で押したい場合
    for i in range(30):
        ag.press('right')
        ag.press('down')
        ag.hotkey('ctrl', 'v') # ペースト

    # 文字入力を英数字に戻す
    ag.press('kanji')

    # 「名前を付けて保存」を開く
    ag.hotkey('fn','f12')

    # ファイ保存のパスを書く
    ag.typewrite(save_file_path)

    # excelファイルを保存する
    ag.press('enter')
    time.sleep(1)

    # メニューバーを操作する
    ag.hotkey('alt', 'f')

    # ブックを閉じる
    #ag.press('c') 

    # エクセルを終了する
    ag.hotkey('alt','f4')
    print(save_file_path)

if __name__ == '__main__':
    # ディレクトリパス
    dir_path = os.getcwd()

    # RPA関数の実行
    memo_func() #メモ帳
    excel_func() #エクセル
    
    print('finished')

(重要)USキーボード以外でのpyautoguiの不具合対策

ファイルを保存するには、絶対パスを入力して保存するのが確実で安心です。
ところが、USキーボード以外では次のような不具合があります。

Python 3.x - pyautoguiを用いて「:」の入力をしたいです。|teratail

万が一のリンク切れに備えて、要点を以下に記します。
D:\program\python\14_file と絶対パスを入力したいが
D*\program\python\14_file と、「:」が「*」となる問題です。

これを解決するには、例えば、次のような場所にある「_pyautogui_win.py」を開いて、関数_keyDownの中にあるneedsSift以下に、次の3行を追加します。

C:\Users\[ユーザー名]\Anaconda3\pkgs\pyautogui-0.9.48-py36h9f0ad1d_1\Lib\site-packages\pyautogui\_pyautogui_win.py

def _keyDown(key):
    # 略
    needsShift = pyautogui.isShiftCharacter(key)

    # 次の3行を追加
    if key == '@': needsShift = False
    if key == '^': needsShift = False
    if key == ':': needsShift = False

    """
    # OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start.
    if key in keyboardMapping.keys():
    # 略

以上

<広告>