RPA ChallengeをあえてAutomation Anywhereで解いてみる

# RPA Challengeとは
[RPA Challenge](http://www.rpachallenge.com/?lang=ja)はUiPath社によって運営されている、自動化が難しいWebサイトを創意工夫して速く正確に自動化する課題を提供するサイトです。通常はUiPathのロボットを使って課題を解くのでしょうか、ここは敢えてAutomation Anywhere社が提供しているRPAで解いてみるということをしたいと思います。

**使用環境**

* Automation Anywhere Enterprise v11.3.2 / v11.3.3

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c13f48bd-0838-d155-0503-1b1bad04dc5f.png)

# 課題の概要
「Input Forms」という課題には日本語も用意されています。これは、ダウンロードしたExcelファイルの中にあるダミーの個人情報をRPAでWebフォームに入力していく課題です。ただし、Webページのフォーマットは登録ボタンを押すごとに毎回変わり、入力フィールドの位置、順番は一定ではありません。加えて、フィールドにつけられているIDも "x05y7" などランダムな文字列で毎回異なるため、DOMやXPathでフィールドを特定することも困難です。

たとえば、フォームのHTMLは以下のような形です。いまはメールアドレス、住所、...の順番で並んでいますが、この並び方は毎回異なり、レイアウトも異なることがあります。

```HTML:
<form _ngcontent-c1="" novalidate="" ng-reflect-form="[object Object]" class="ng-untouched ng-pristine ng-invalid">

<div _ngcontent-c1="" class="row"><!--bindings={
  "ng-reflect-ng-for-of": "[object Object],[object Object"
}-->

<div _ngcontent-c1="" class="col s6 m6 l6">
<rpa1-field _ngcontent-c1="" _nghost-c2="" ng-reflect-id="8iWmb" ng-reflect-dictionary-value="メールアドレス" ng-reflect-label="labelEmail" ng-reflect-parent-form-group="[object Object]">
  <div _ngcontent-c2="" ng-reflect-form="[object Object]" class="ng-untouched ng-pristine ng-invalid">
    <label _ngcontent-c2="">メールアドレス</label>
    <input _ngcontent-c2="" ng-reflect-name="labelEmail" id="8iWmb" name="8iWmb" class="ng-untouched ng-pristine ng-invalid">
  </div>
</rpa1-field>
</div>

<div _ngcontent-c1="" class="col s6 m6 l6">
<rpa1-field _ngcontent-c1="" _nghost-c2="" ng-reflect-id="kFce2" ng-reflect-dictionary-value="住所" ng-reflect-label="labelAddress" ng-reflect-parent-form-group="[object Object]">
  <div _ngcontent-c2="" ng-reflect-form="[object Object]" class="ng-untouched ng-pristine ng-invalid">
    <label _ngcontent-c2="">住所</label>
    <input _ngcontent-c2="" ng-reflect-name="labelAddress" id="kFce2" name="kFce2" class="ng-untouched ng-pristine ng-invalid">
  </div>
</rpa1-field>
</div>

...

<input _ngcontent-c1="" class="btn uiColorButton" type="submit" value="登録">
</form>
```
例1:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/555cae25-25c9-82a4-47f8-816a0e340467.png)
例2:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c5102300-c3e4-57e6-59cc-046f054714e0.png)
例3:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/b89a8468-7718-a8d9-362e-ec5778831cf9.png)

# Automation Anywhere による結果
今回は、試行錯誤の結果「約7秒で正解率100%でクリア」までもっていくことができました。さらなるチューニングをAutomation Anywhere上でできるかどうか、試してみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/88d0c753-3b57-3479-6492-733747c4acba.png)

# (以下ネタバレ含む) 課題の解き方
以下の章では、この課題の解き方を考え方とともに順を追ってみていきます。ネタバレを含みますので、自分で解きたい人は見ないでください。

### 課題への着眼点
RPA Challengeの課題を解くポイントですが、登録フォームが毎回変わる中で、「**普遍的に変わらないものを見極め、それをうまく利用する**」ことです。今回の場合は`label`に表示される7種類のテキストは同じで、かつ`label`の次のコントロールとして必ず`input`が来るように見えるので、そのことを利用して、まず特定の`label`を探して、その次に位置する`input`に、対応する値を代入するようにすれば解決しそうです。

### 特定フィールドへの入力手段を確立する
では、実際にやってみましょう。Internet ExplorerでRPA Challengeのページを表示したら、Object Cloningコマンドを使って「会社名」のラベルを捕捉してみましょう。コマンドをドラッグ＆ドロップすると、以下のようなダイアログボックスが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/a6eb6e97-031f-b96f-f01c-6c8244f1b328.png)

「リフレッシュ」ボタンを押してドロップダウンから「Rpa Challenge - Internet Explorer」というウィンドウを選択して「キャプチャ」ボタンを押します。

以下のように、`LABEL`のHTML Tagが取得できていれば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/39ba3164-81c7-d2d0-b1bb-d84c5bbad4db.png)

「検索条件を選択」の右側にある「検索条件を展開 (![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/ea46b4ab-eddb-64f2-7196-ed6385887463.png))」ボタンをクリックすると、その他に取得できたプロパティの一覧も表示されます。「HTML Inner Text」に「会社名」というラベル名が取得できていると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/21cf416a-d450-eb8d-0127-097911bc7d5f.png)

そして、`input`フィールドが常に`label`の次に来ることを思い出しましょう。そして、HTMLフォームでは、「`TAB`キーを押すことで次のコントロールに移れる」という一般的な動作規則を使って、捕捉した`label`コントロールの隣の`input`フィールドにフォーカスを移動することにします。

先ほどの「会社名」`label`を補足したObject Cloningコマンドのダイアログボックスで、「実行するアクションの選択」から「Click」を選んでおきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/5d10ac9f-1b1c-6ae8-782d-a6b28b3fb157.png)
`label`をクリックしておくことで、このコントロールに最初にフォーカスを合わせておくことができます。`TAB`キーを押すと、次の`input`フィールドにフォーカスを移せます。フォーカスを移した後は、`input`フィールドに、ひとまずラベルと同じ「会社名」と入力しておくことにしましょう。`TAB`キーと文字列の入力はInsert Keystrokesコマンドを使います。「キーボード操作」のフィールドには、特殊キーとしてダイアログボックスの下側の仮想キーボードから`TAB`を押して入力します。(テキストで"[TAB]"と直接入力してもOK)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/9d44793b-121d-9e6c-c959-7c60da9f76e7.png)

そうすると、以下のようなアクションリストが出来上がります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/8955f003-3b2a-cf9d-46e2-a8989e0a4ca9.png)

これで、「会社名」フィールドがいろいろな位置に出てきたときに正しく文字が入力できるか試してみてください。正しいフィールドに文字が入らない時 (たとえばInternet Explorerのアドレス部分に入ってしまうなど)は、Object Cloningコマンドで`label`コントロールを正しく捕捉できていないことになります。

先ほどのObject Cloningダイアログボックスに戻り、オブジェクトの検索条件をON/OFFしていろいろ試行錯誤してみましょう。双眼鏡マーク (![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/7ff39c1b-46ac-3647-b67a-5892481c2d43.png))がついているプロパティが検索対象になっています。試行錯誤をした結果、今回は、HTML Inner Textのみを検索条件に指定するとうまくいくことがわかりました。つまり、**「会社名」という文字列が埋め込まれているコントロールを検索してくる**、という条件を指定するとうまくいく、ということになります。

ためしに「会社名」と「苗字」の2つのフィールドに連続して文字列を入れるアクションリストを組んで試してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c1768fe3-540a-4e40-5ad4-b976842c8428.png)

結果は以下のようになるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/11f1c3fe-4360-9820-302e-ee17498d62ba.png)
これで、あとはフィールドの数だけアクションをコピーしていけば、全フィールドへの入力ができるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/2716c250-1539-522e-b658-87dc749cf2be.png)

アクションリストと結果はこんなかんじです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/5278f1d2-1abd-6258-e133-0720a6d3a5a8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/f732ee9c-2041-5624-de2c-f35e1407a079.png)



### Excel からの入力部分を作る
ここまで出来たら、次にいままでダミーで`input`フィールドに入れていた文字列をExcelから読み込んだ文字列で置き換えます。RPA Challengeのページからダウンロードできるchallenge_ja.xlsxの内容は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/3423bcff-e86e-65e0-25cf-5969b66f0c9c.png)
>注: なぜかGet All Cellsコマンドでエラーが出てしまったため、このシートのデータの書式をすべて落としてchallenge_ja1.xlsxとして保存して、これを使用しました。

「[RPAでExcelを操作する際のヒント (Automation Anywhere編)](https://qiita.com/RPAbot/items/5e513aa259a595182dd0)」の**Loop > Each Row in an Excel datasetのサンプル**に掲載されているサンプルに倣い、Excelファイルからの読み込み部分を作成します。以下はExcelの表から読み込むサンプルです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/5e1cf354-edf0-04c7-7567-6671f1135d32.png)
また、ループの最後に最後に「登録」ボタンをクリックする動作を加えます。あと、Get All CellsコマンドでExcelデータを読み込んだ直後に「開始」ボタンを押す操作も加えます。

>注: Object Cloningでうまく目的のコントロールが選択できないときは、プロパティの値を手入力してみましょう。Firefoxのウェブ開発モードで使えるインスペクターでタグを調べるとよいでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/e6ca8177-c0a5-9702-bcd5-723970c528c4.png)

```HTML:開始ボタン
<button _ngcontent-c1="" class="waves-effect col s12 m12 l12 btn-large uiColorButton">開始</button>
```
検索条件:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/aee96a52-cbe2-7a3d-8a97-b2daf0a7b319.png)

```HTML:登録ボタン
<input _ngcontent-c1="" class="btn uiColorButton" type="submit" value="登録">
```
検索条件:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/35b0ec09-0837-0a54-8733-4344108a1073.png)

追加後のアクションリストがこちらです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/49d3ce73-1265-3935-a40b-efd85b314571.png)

このアクションリストでボットをv11.3.2 実行した結果、以下のような結果となりました。(正解率約70%、所要時間約30秒)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/6ce64375-1969-9ca3-ff45-a21eccb59cc6.png)


### エラー率の改善とスピードチューニング
ここまでで課題が大きく2点あります。ひとつは正解率が低いことです。これは、実行中の様子を注意深く見ていると、正しいフィールドを選べているかについては100%の正解率でしたが、入力される文字列の前 or 後ろが切れて入力されることがあるようでした。これはキーストロークのエミュレーションによるタイミングの問題と思われます。2つ目は時間がかかりすぎることです。期待値の約10倍の時間がかかっています。これも同じくキーストロークのエミュレーションが遅いことによるものと思われます。つまり、両方ともアクションリストでいうと9, 12, 15, 18, 21, 24, 27行目をチューニングする必要があります。

ちなみに、v11.3.3で実行してみると、正解率98% (1つ間違え)、所要時間約38秒となりました。バージョンによって結構違いますね。Keystokeでのタイミングの取られ方が変わったようで、キーストロークによる入力漏れが圧倒的に減りましたが、時間がかかるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/cbe20bea-9761-7a46-9111-a9e7f120826e.png)

そこで、キーストロークやラベルにタブを打って次のコントロールを指定するのをやめて抜本的にやり方を変えることにしました。HTMLをよく見てみると、`input`フィールドの順番が変わっても、属性でフィールドの種類がわかるものがあることがわかりました。

```HTML:
ng-reflect-name="labelAddress" ⇒ 苗字フィールド
ng-reflect-name="labelAddress" ⇒ 名前フィールド
ng-reflect-name="labelAddress" ⇒ 会社名フィールド
ng-reflect-name="labelAddress" ⇒ 部署フィールド
ng-reflect-name="labelAddress" ⇒ 住所フィールド
ng-reflect-name="labelAddress" ⇒ メールアドレスふぃーづろ
ng-reflect-name="labelAddress" ⇒ 電話番号フィールド
```

そこで、Manage Web Controlコマンドを使って各`input`フィールドをキャプチャーして、識別に使う属性を手で変更することにしました。たとえば、最初の「苗字」フィールドをキャプチャしてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/83e87cc9-b5e8-a2c9-47b6-43c46bad3d07.png)
キャプチャーした直後のManage Web Controlコマンドのダイアログボックスは以下の通りです。そこに<font color="Red">赤字</font>で書いた変更を加えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/ebd5d864-ef16-b771-837f-004591b11172.png)
変更後のダイアログボックスは以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/c5bf9e03-362f-894e-2f0d-07b16f0f3df6.png)

これをあとの6つのフィールドに対しても行います。

### 最終的なアクションリスト
最終的には、さきほどのものよりも行数が少なくシンプルなものになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/8b3f491b-1cc5-c5b4-3405-6f2c8a9c1a3e.png)

これをv11.3.3で実行してみると、先ほどよりもだんぜんはやくなりました。正解率100%、約7秒で完了です。だいぶチューニングされました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/88d0c753-3b57-3479-6492-733747c4acba.png)

これ以上チューニングの余地があるのかどうかわかりませんが、皆さんもぜひ試してみてください。