PythonでRPA

パソコン操作の自動化というと、何年か前ならUWSCやロケットマウスなどのソフトウェアが人気でした。

あの頃は、マクロ、マクロって言ったものですけど、今どきはRPAって言われてるみたいですね。

私も10年くらい前にUWSCを使ってネットゲームの自動化を競い合ってました。

どれだけ人間のように動かせるかがテーマで、 三平方の定理を使って敵の位置をクリックして戦わせたり、 移動狩りしたり、店売りをしたり、 内部メモリを読み取ってインベントリのアイテムを判別してアイテム整理をしたり、 近くに人が通りかかったらログアウトして逃げたりと、色々と工夫しました。

内部メモリを読み取る技術があると、もの凄く柔軟に動かせるんですよね。(人間よりも視野が広くなる)

UWSCは私の中でベスト・オブ・神ソフトです。

最近のRPAブームで、久しぶりに自動化プログラムを組んでみようと思ったら、UWSCの作者さんが行方不明という衝撃的な記事を見つけました。

現在は更新も止まり、pro版も購入出来なくなっているみたいです。(RPAブームで何かあったのかな・・・)

PythonにもWindows自動化ライブラリがあるということで、そちらを勉強がてら試してみることにしました。

(Powerなんちゃらとかいうwindows純正アプリも使ってみましたが色々と・・・乙でした。ウィンドウサイズ変更に5秒待たされたり、ローカルに設定を保存出来なかったり、ショートカットを作成できなかったり。)

PythonのRPAライブラリ

pythonを使ってWindowsを自動化するには、pyautoguiライブラリをインストールします。

ライブラリをインポートし、pyautogui.moveTo(x, y, ms)のように書けば、マウスを動かすことが出来ます。

ただ、UWSCのMMVコマンドに比べるとやや冗長なコードです。

RPAに必要なのはどれだけ簡単に目的を達成できるかですよね。

クラスもハンドルも、できるだけ無いほうがユーザーライクです。

ということで、UWSCライクな関数群を作ってみました。

関数名は全部大文字、引数は極力減らす、遷移確認も込み。

import pyautogui
import win32gui
import win32com.client
import time
import subprocess
import os
import ctypes

# 初期化
def RPA_INIT():
    SET_CURDIR()

# カレントディレクトリを実行パスと同じフォルダにセットする
def SET_CURDIR():
    os.chdir(os.path.dirname(os.path.abspath(__file__))) 

# マウスを動かす
def MMV(x, y, ms=0):
    pyautogui.moveTo(x, y, ms)

# マウス位置を取得
def MPOS():
    return pyautogui.position()

# 指定座標をクリック
def CLICK(x=None, y=None):
    if x is None or y is None:
        pyautogui.click()
    else:
        pyautogui.click(x, y)

# 指定座標を右クリック
def RCLICK(x=None, y=None):
    if x is None or y is None:
        pyautogui.click(button="right")
    else:
        pyautogui.click(x, y, button="right")

# 指定座標をダブルクリック
def DBLCLICK(x=None, y=None):
    if x is None or y is None:
        pyautogui.click(clicks=2)
    else:
        pyautogui.click(x, y, clicks=2)

# キーを押す
def KEY(key):
    pyautogui.press(key)

# 複数のキーを打つ
def TYPING(text):
    pyautogui.write(text)

# シフトを押す
def DOWNSHIFT():
    pyautogui.keyDown('shift')

# SHIFTを離す
def UPSHIFT():
    pyautogui.keyUp('shift')

# Ctrlを押す
def DOWNCTRL():
    pyautogui.keyDown('ctrl')

# Ctrlを離す
def UPCTRL():
    pyautogui.keyUp('ctrl')

# Shift + Key
def SHIFTKEY(key):
    pyautogui.hotkey('shift', key)

# Ctrl + Key
def CTRLKEY(key):
    pyautogui.hotkey('ctrl', key)

# 画像をクリック
def CLICK_IMG(img):
    pt = pyautogui.locateCenterOnScreen(img)
    CLICK(pt[0], pt[1])

# 画像をダブルクリック
def DBLCLICK_IMG(img):
    pt = pyautogui.locateCenterOnScreen(img)
    if pt is not None:
        DBLCLICK(pt[0], pt[1])

# 画像が見つかるまで待つ
def WAIT_IMG(img, timeout=10):
    while(1):
        pt = pyautogui.locateCenterOnScreen(img)
        if pt is not None:
            return pt
        time.sleep(0.1)
        timeout -= 0.1
        if timeout <= 0:
            break
    return None

# 画像をクリックするまで待つ
def WAIT_CLICK_IMG(img):
    pt = WAIT_IMG(img)
    CLICK(*pt)

# ウィンドウを最前面にする
def ACTWIN(hwnd):
    shell = win32com.client.Dispatch("WScript.Shell")
    shell.SendKeys('%')
    win32gui.SetForegroundWindow(hwnd)

# Shellコマンド実行
def EXEC(path, arg=""):
    #win32api.ShellExecute(0, path, arg, None, ".", 0)
    #RunAs(path, "", arg)
    #ctypes.windll.shell32.ShellExecuteW(0, 0, path, arg, 0, 1)
    subprocess.Popen([path, arg], shell=False)

# フォルダを開く
def OPEN_FOLDER(path):
    subprocess.Popen(['explorer', path], shell=True)

# フォルダを開いて移動させる
def OPEN_FOLDER_PLACE(path, x, y, width, height):
    name = os.path.basename(path)
    hwnd = FIND_WINDOW(name)
    if hwnd == 0:
        OPEN_FOLDER(path)
    else:
        ACTWIN(hwnd)
        
    hwnd = WAIT_WINDOW(name)
    if hwnd != 0:
        print(str(hwnd))
        MOVE_WINDOW(hwnd, x, y, width, height)

# ウィンドウを探す
def FIND_WINDOW(name):
    return win32gui.FindWindow(None, name)

# ウィンドウが見つかるまで待つ
def WAIT_WINDOW(name, timeout=3):
    while(1):
        hwnd = FIND_WINDOW(name)
        if hwnd != 0:
            return hwnd
        time.sleep(0.1)
        timeout -= 0.1
        if timeout <= 0:
            break
    return 0

# ウィンドウを移動させる
def MOVE_WINDOW(hwnd, x, y , width, height):
    win32gui.MoveWindow(hwnd, x, y, width, height, True)

Seleniumと組み合わせる

pythonにはSeleniumというブラウザ操作を自動化するライブラリがあります。

Seleniumはウェブサイトを開いてIDとパスワードを入力して自動ログインしたり、サイトからデータを抽出したりとネットの自動情報収集に使われています。(スクレイピング)

このSeleniumにも弱点があって、ブラウザ外の挙動には手を出せません。

例えば、phpmyadminやルーターなどにアクセスするときに、ブラウザ上部にパスワード入力がポップアップするとそれ以上進めなくなります。

Python RPAと組み合わせるとこの弱点が無くなります。

Seleniumの操作をUWSCライクに書き直しました

driverはグローバル変数で定義して、存在を意識せず使えるようにする。

ブラウザはChromeに絞る。OPEN_WEBだろうがWEB_OPENだろうが使える。

テキストボックスにテキストを入力するなら要素名とテキストだけあれば良い。

と言ったコンセプト。

と言ってもまだまだ作りかけです。

import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import chromedriver_binary
from selenium.webdriver.common.keys import Keys

def OPEN_BROWSER(user_name=None, profile=None):
    global driver
    options = Options()
    
    if user_name is None:
        user_profile = 'C:\\Users\\username\\AppData\\Local\\Google\\Chrome\\User Data\\Selenium'
    else:
        user_profile = 'C:\\Users\\' + user_name + '\\AppData\\Local\\Google\\Chrome\\User Data\\Selenium'

    if profile is None:
        profile = "Default"

    options.add_argument('--user-data-dir=' + user_profile)
    options.add_argument('--profile-directory=' + profile)
    driver = webdriver.Chrome(options=options)
    
def OPEN_BROWSER_GUEST(user_name=None, profile=None):
    global driver
    options = Options()
    
    if user_name is None:
        user_profile = 'C:\\Users\\username\\AppData\\Local\\Google\\Chrome\\User Data\\Selenium'
    else:
        user_profile = 'C:\\Users\\' + user_name + '\\AppData\\Local\\Google\\Chrome\\User Data\\Selenium'

    if profile is None:
        profile = "Default"

    options.add_argument('--incognito')
    driver = webdriver.Chrome(options=options)


def OPEN_URL(url):
    global driver
    driver.get(url)

def WEB_OPEN(url):
    OPEN_URL(url)

def OPEN_WEB(url):
    OPEN_URL(url)

def URL_OPEN(url):
    OPEN_URL(url)

def NEW_TAB():
    global driver
    driver.execute_script("window.open()")
    new_window = driver.window_handles[1]
    driver.switch_to.window(new_window)



def WEB_INPUT(element, text):
    global driver
    elem = driver.find_element_by_name(element)
    elem.send_keys(text)

def IS_NAME(name):
    global driver
    try:
        elem = driver.find_element_by_name(name)
        return True
    except:
        return False


def WEB_SUBMIT(element):
    global driver
    try:
        elem = driver.find_element_by_name(element)
    except:
        elem = driver.find_element_by_class_name(element)
        return

    url = GET_URL()
    elem.submit()
    WAIT_CHANGE_URL(url)

def WEB_CLICK(text):
    global driver
    try:
        href = driver.find_element_by_partial_link_text(text).click()
    except:
        return

def IS_OPEN_DRIVER():
    global driver
    try:
        url = driver.get_window_size()
        return True
    except:
        CLOSE_BROWSER()
        return False

def CLOSE_BROWSER():
    global driver
    driver.quit()

def WAIT_BROWSER_CLOSE():
    while(1):
        if not IS_OPEN_DRIVER():
            CLOSE_BROWSER()
            break
        time.sleep(1)

def WAIT_CHANGE_URL(url, timeout=10):
    global driver
    while(1):
        if url != driver.current_url:
            return
        time.sleep(0.1)
        timeout -= 0.1
        if timeout <= 0:
            break

def GET_URL():
    return driver.current_url

使用例

XServerのサーバーパネルにログインして、phpmyadminをクリック、パスワードダイアログが表示されたらIDとパスワードを入力してOKボタンをクリック、エクスポートタブを開き、実行ボタンを押すというマクロです。

from RPA import *
from WebRPA import *

RPA_INIT()
OPEN_BROWSER("pcuser","Profile 1")

# XServer - サーバーパネルログイン
WEB_OPEN("https://www.xserver.ne.jp/login_server.php")
WEB_INPUT("username", 'xserverusername')
WEB_INPUT("server_password", 'xserverpassword')

WAIT_CLICK_IMG("Login.png")

# PHPMyAdmin
WAIT_IMG("php.png")              # PHPのアイコン画像の一部
WEB_CLICK("phpmyadmin")          # ブラウザ内のテキストをクリック

# パスワード入力画面
pt = WAIT_IMG("Login2.png")    # ブラウザのパスワード入力ダイアログのOKボタンの一部
if pt is not None:
    TYPING("phpmyadminuser")     # ID
    KEY("tab")
    TYPING("phpmyadminpass")     # パスワード
    CLICK(*pt)

# エクスポート
WAIT_CLICK_IMG("export.png")     # エクスポートタブの一部

# 実行ボタン
WAIT_CLICK_IMG("jikkou.png")     # 実行ボタンの一部

WAIT_BROWSER_CLOSE()             # ブラウザが閉じられるまでループ待機

Seleniumで対応出来ない部分を上手く画像認識でカバーしています。

完成したpyファイルは .pyautoみたいな拡張子に変更して、python.exeと関連付けるとダブルクリックで実行できるようになります。

最強のRPA環境

pythonなのでwebapiと連携して挙動を変えたり、GUIを作ったりとUWSCよりも遥かに複雑なことができます。

RPA機能自体をどんどん拡張出来ますし、これにプロセスメモリの読み取り機能でもつければ、もう無敵ですね。

これで、どんな自動化も任せて・・・

肝心の業務が無い(゚∀゚)

というオチでした(´;ω;`)ブワッ

おすすめ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です