【RPA】業務で使うWebアプリの操作を自動化してみた

# はじめに
給与・賞与明細、源泉徴収票、標準報酬月額決定通知書などをWebで参照できる[PayBrowser](https://www.ags.co.jp/service/paybrowser.html)というプロダクトがあるのですが、その自動操作スクリプトを作ってみます。
このプロダクトは全従業員が使うもので、初回は仮パスワードでログインする運用です（ちなみに従業員と言った場合、パート／アルバイトは含みますが役員は含みません）。
仮パスワードは人事総務部が発行するのですが、プロダクト側に一括で変更する機能がなく、対象の従業員を検索で一人ずつ呼び出しては変更するという単調な操作を繰り返していました。多い月には100名を超える新規入社者があり、手作業でやるのは一苦労です。

ここは**RPA**（Robotic Process Automation）に任せましょう。

# 実現方式
ブラウザをシナリオ通りに操作する技術には以下があると思います。

1. [Power Automate Desktop](https://flow.microsoft.com/ja-jp/desktop/)
2. [Seleniumフレームワーク](https://www.selenium.dev/documentation/ja/)
3. [Microsoft Edge ドライバー](https://developer.microsoft.com/ja-jp/microsoft-edge/tools/webdriver/)
4. VBAやPowerShellから Internet Explorer のCOMオブジェクトを使う
5. [WinActor](https://winactor.com/)や[UiPath](https://www.uipath.com/ja/)、[Automation Anywhere](https://www.automationanywhere.com/jp/)などのRPA製品

**Selenium**については以前、Pythonを用いて自動化したプログラムを[寄稿](https://qiita.com/mindwood/items/2f58f450f382a5825ab9)しているので興味がある人は見て下さい。

**UiPath**は無料で使えるCommunity Editionがあり、[Qiitaの記事数の多さ](https://qiita.com/tags/uipath)からも分かる通りエンジニアに人気があります。

ユーザー部門に運用させることを考えると、今なら**Power Automate Desktop**が本命かなと思います。Microsoft社が2020年に買収したSoftomotive社のWinAutomationがベースになっているのですが、Windowsの標準アプリとして搭載される予定で、これは大きなアドバンテージになるでしょう。

今回は現場の制約としてWindowsの管理者権限がありません。
社内標準から外れたソフトをインストールするには情シスに申請が必要なのですが、大きな組織なので調整に手間がかかりそうです。
Windows標準機能だけで実現させたいので、20年以上前から使われて今更という感のある、項番4のCOMオブジェクトを採用します。IEが2022年に廃止されるタイミングでPower Automate Desktopに移行すれば良いと割り切りました。

# 実装
PowerShellからCOMオブジェクトを叩いても良いのですが、元データがExcelで来るのでVBAでやってみます。

## 入力データの作成
人事システムから取得し、Excelに貼り付けるだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/a6b72026-9c1b-6385-f57b-c0afecd873bb.png)

## 入力データの読み込み
カーソル位置を起点として、最終行まで処理します。
毎回先頭から読むように作ってしまうと、途中で失敗したときにリカバリーが面倒だからです。

```vb
Set ws = ThisWorkbook.Sheets("新規入社者")

i = ActiveCell.Row  'カーソル行の取得
If ws.Cells(i, 1) = "" Then
    MsgBox "カーソル位置がおかしいです"
    Exit Sub
End If

If MsgBox(ws.Cells(i, 1) & " " & ws.Cells(i, 2) & " から開始します", vbOKCancel) = vbCancel Then
    Exit Sub
End If

'最終行までループ
Do Until ws.Cells(i, 1) = ""
    従業員番号 = ws.Cells(i, 1)
      :
    i = i + 1
Loop
```

## ブラウザの操作
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/778256a3-68af-1835-fb2e-51e25d7dc682.png)
IEを制御するオブジェクトを得るには次のようにします。
#### 新規IEの場合
```vb
Set oIE = CreateObject("InternetExplorer.Application")
```

#### 既に起動しているIEの場合
```vb
Set oShell = CreateObject("Shell.Application")
For Each oWin In oShell.Windows
    If oWin.Name = "Internet Explorer" Then
        If oWin.Document.Title = "PayBrowser管理 : 従業員一覧" Then  'IEが複数起動していることがあるのでタイトルで特定
            Set oIE = oWin
            Exit For
        End If
    End If
Next
```

PayBrowserに管理者アカウントでログインして従業員一覧の画面を出すところまでは、人間に操作してもらうことにしました。

### 従業員番号のセット

```vb
oIE.Document.GetElementsByName("empCd")(0).Value = 従業員番号
```

このDOM要素にはidが定義されていなかったので、*GetElementsByName*メソッドで返される配列の先頭で特定します。

### 検索ボタンのクリック
SeleniumやMicrosoft WebDriverなら組込みライブラリでもう少しシンプルに書けますが、COMオブジェクトを直接操作するやり方なら、汎用的な関数を自前で用意しておくとプログラム全体の見通しが良くなります。

例えば以下は、ボタン名でクリックする関数（メソッド）です。

```vb
Public Function ButtonClick(ByRef objIE As Object, buttonValue As String)
    Dim objInput As Object

    For Each objInput In objIE.Document.getElementsByTagName("INPUT")
        If objInput.Value = buttonValue Then
            objInput.Click
            Exit For
        End If
    Next
End Function
```

この関数を使うと、"検索" と記されたボタンをこんな感じでクリックできます。

```vb
ButtonClick oIE, "検索"
```

IEのオブジェクトをメンバ変数とする**クラスモジュール**でカプセル化しても良いと思います。

### 成功判定
同様の手順で、仮パスワードのセットやボタンをクリックし、最終的に返されるHTMLソース（`innerHTML`）にキーとなるメッセージが含まれていれば成功と言えるので次の従業員に進みます。

```vb
If InStr(oIE.Document.GetElementById("main").innerHTML, "仮パスワードを発行しました") = 0 Then
    MsgBox "エラー：" & 従業員番号
    Exit Sub
End If
```

# さいごに
昨今のWebアプリケーションはSPAが主流になりつつあるので、ブラウザの自動操作は複雑化する一方ですが、比較的古いWebアプリケーションでは簡単に実装できました。