RPA Challengeとは
RPA ChallengeはUiPath社によって運営されている、自動化が難しいWebサイトを創意工夫して速く正確に自動化する課題を提供するサイトです。通常はUiPathのロボットを使って課題を解くのでしょうか、ここは敢えてAutomation Anywhere社が提供しているRPAで解いてみるということをしたいと思います。
使用環境
- Automation Anywhere Enterprise v11.3.2 / v11.3.3
課題の概要
「Input Forms」という課題には日本語も用意されています。これは、ダウンロードしたExcelファイルの中にあるダミーの個人情報をRPAでWebフォームに入力していく課題です。ただし、Webページのフォーマットは登録ボタンを押すごとに毎回変わり、入力フィールドの位置、順番は一定ではありません。加えて、フィールドにつけられているIDも "x05y7" などランダムな文字列で毎回異なるため、DOMやXPathでフィールドを特定することも困難です。
たとえば、フォームの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>
Automation Anywhere による結果
今回は、試行錯誤の結果「約7秒で正解率100%でクリア」までもっていくことができました。さらなるチューニングをAutomation Anywhere上でできるかどうか、試してみてください。
(以下ネタバレ含む) 課題の解き方
以下の章では、この課題の解き方を考え方とともに順を追ってみていきます。ネタバレを含みますので、自分で解きたい人は見ないでください。
課題への着眼点
RPA Challengeの課題を解くポイントですが、登録フォームが毎回変わる中で、「普遍的に変わらないものを見極め、それをうまく利用する」ことです。今回の場合はlabel
に表示される7種類のテキストは同じで、かつlabel
の次のコントロールとして必ずinput
が来るように見えるので、そのことを利用して、まず特定のlabel
を探して、その次に位置するinput
に、対応する値を代入するようにすれば解決しそうです。
特定フィールドへの入力手段を確立する
では、実際にやってみましょう。Internet ExplorerでRPA Challengeのページを表示したら、Object Cloningコマンドを使って「会社名」のラベルを捕捉してみましょう。コマンドをドラッグ&ドロップすると、以下のようなダイアログボックスが表示されます。
「リフレッシュ」ボタンを押してドロップダウンから「Rpa Challenge - Internet Explorer」というウィンドウを選択して「キャプチャ」ボタンを押します。
以下のように、LABEL
のHTML Tagが取得できていれば成功です。
「検索条件を選択」の右側にある「検索条件を展開 ()」ボタンをクリックすると、その他に取得できたプロパティの一覧も表示されます。「HTML Inner Text」に「会社名」というラベル名が取得できていると思います。
そして、input
フィールドが常にlabel
の次に来ることを思い出しましょう。そして、HTMLフォームでは、「TAB
キーを押すことで次のコントロールに移れる」という一般的な動作規則を使って、捕捉したlabel
コントロールの隣のinput
フィールドにフォーカスを移動することにします。
先ほどの「会社名」label
を補足したObject Cloningコマンドのダイアログボックスで、「実行するアクションの選択」から「Click」を選んでおきましょう。
label
をクリックしておくことで、このコントロールに最初にフォーカスを合わせておくことができます。TAB
キーを押すと、次のinput
フィールドにフォーカスを移せます。フォーカスを移した後は、input
フィールドに、ひとまずラベルと同じ「会社名」と入力しておくことにしましょう。TAB
キーと文字列の入力はInsert Keystrokesコマンドを使います。「キーボード操作」のフィールドには、特殊キーとしてダイアログボックスの下側の仮想キーボードからTAB
を押して入力します。(テキストで"[TAB]"と直接入力してもOK)
これで、「会社名」フィールドがいろいろな位置に出てきたときに正しく文字が入力できるか試してみてください。正しいフィールドに文字が入らない時 (たとえばInternet Explorerのアドレス部分に入ってしまうなど)は、Object Cloningコマンドでlabel
コントロールを正しく捕捉できていないことになります。
先ほどのObject Cloningダイアログボックスに戻り、オブジェクトの検索条件をON/OFFしていろいろ試行錯誤してみましょう。双眼鏡マーク ()がついているプロパティが検索対象になっています。試行錯誤をした結果、今回は、HTML Inner Textのみを検索条件に指定するとうまくいくことがわかりました。つまり、「会社名」という文字列が埋め込まれているコントロールを検索してくる、という条件を指定するとうまくいく、ということになります。
ためしに「会社名」と「苗字」の2つのフィールドに連続して文字列を入れるアクションリストを組んで試してみましょう。
結果は以下のようになるはずです。
これで、あとはフィールドの数だけアクションをコピーしていけば、全フィールドへの入力ができるようになります。
Excel からの入力部分を作る
ここまで出来たら、次にいままでダミーでinput
フィールドに入れていた文字列をExcelから読み込んだ文字列で置き換えます。RPA Challengeのページからダウンロードできるchallenge_ja.xlsxの内容は以下の通りです。
注: なぜかGet All Cellsコマンドでエラーが出てしまったため、このシートのデータの書式をすべて落としてchallenge_ja1.xlsxとして保存して、これを使用しました。
「RPAでExcelを操作する際のヒント (Automation Anywhere編)」のLoop > Each Row in an Excel datasetのサンプルに掲載されているサンプルに倣い、Excelファイルからの読み込み部分を作成します。以下はExcelの表から読み込むサンプルです。
また、ループの最後に最後に「登録」ボタンをクリックする動作を加えます。あと、Get All CellsコマンドでExcelデータを読み込んだ直後に「開始」ボタンを押す操作も加えます。
注: Object Cloningでうまく目的のコントロールが選択できないときは、プロパティの値を手入力してみましょう。Firefoxのウェブ開発モードで使えるインスペクターでタグを調べるとよいでしょう。
<button _ngcontent-c1="" class="waves-effect col s12 m12 l12 btn-large uiColorButton">開始</button>
<input _ngcontent-c1="" class="btn uiColorButton" type="submit" value="登録">
このアクションリストでボットをv11.3.2 実行した結果、以下のような結果となりました。(正解率約70%、所要時間約30秒)
エラー率の改善とスピードチューニング
ここまでで課題が大きく2点あります。ひとつは正解率が低いことです。これは、実行中の様子を注意深く見ていると、正しいフィールドを選べているかについては100%の正解率でしたが、入力される文字列の前 or 後ろが切れて入力されることがあるようでした。これはキーストロークのエミュレーションによるタイミングの問題と思われます。2つ目は時間がかかりすぎることです。期待値の約10倍の時間がかかっています。これも同じくキーストロークのエミュレーションが遅いことによるものと思われます。つまり、両方ともアクションリストでいうと9, 12, 15, 18, 21, 24, 27行目をチューニングする必要があります。
ちなみに、v11.3.3で実行してみると、正解率98% (1つ間違え)、所要時間約38秒となりました。バージョンによって結構違いますね。Keystokeでのタイミングの取られ方が変わったようで、キーストロークによる入力漏れが圧倒的に減りましたが、時間がかかるようになりました。
そこで、キーストロークやラベルにタブを打って次のコントロールを指定するのをやめて抜本的にやり方を変えることにしました。HTMLをよく見てみると、input
フィールドの順番が変わっても、属性でフィールドの種類がわかるものがあることがわかりました。
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
フィールドをキャプチャーして、識別に使う属性を手で変更することにしました。たとえば、最初の「苗字」フィールドをキャプチャしてみましょう。
キャプチャーした直後のManage Web Controlコマンドのダイアログボックスは以下の通りです。そこに赤字で書いた変更を加えます。
変更後のダイアログボックスは以下の通りです。
これをあとの6つのフィールドに対しても行います。
最終的なアクションリスト
最終的には、さきほどのものよりも行数が少なくシンプルなものになりました。
これをv11.3.3で実行してみると、先ほどよりもだんぜんはやくなりました。正解率100%、約7秒で完了です。だいぶチューニングされました。
これ以上チューニングの余地があるのかどうかわかりませんが、皆さんもぜひ試してみてください。