WebページのUIがUiPathのselectorで上手く取れないときに使えるJavaScriptのDOM API

## TL;DR

- 最近RPAをはじめました
- 要素を探すアクティビティを呼び出す前に`Inject JS Script`で、取りたい要素に任意の`id`を付与するJavaScriptを実行しよう
- そうすることで必ず要素を発見できる
- DOM APIを勉強することでできることはどんどん幅広くなります
- 最後にxamlファイル全文を貼ってます

[tl;dr is 何？](https://qiita.com/n0dam1/items/643e84755d3f40798880)

## はじめに

- 長いです
- UiPathを利用してWebスクレイピング（Webサイトから情報を抽出する）する時のお話です
- Webサイトの中に登場するUIのことを、本記事では**要素**と呼称します
- `Inject JS Script`（JS スクリプトを挿入）アクティビティを利用します
    - [Inject JS Scriptのドキュメント](https://activities.uipath.com/lang-ja/docs/inject-js-script)
- よいJavaScriptコードを書くことが目的ではなく、簡単に取得したい要素をUiPathに認識させることを目的としています
- RPAビギナーによる記事のため、誤りやより高速で安全な方法がありまいたらご指摘ください

### 対象読者

- Webスクレイピングをしたいけれど、うまく取りたい要素を安定してUiPathに認識させることが難しい
- UiPathでの「アクティビティ」「変数」「セレクタ」などはわかってきたけれど、JavaScriptって何…こわい…という人

## JavaScriptを動かしてみる

```javascript
var n = 2;
var hoge = function (arg) {
  return 100 * arg;
}

console.log(hoge(n)); // > 200
```
JavaScript、通称「JS」は Microsoft Edge や Google Chrome などのWebブラウザ上で動作するプログラミング言語です。よくJavaと間違われますが、まったくの別物です。もしこれから勉強しよう！と思ったときに、誤ってJavaの書籍を買ってしまわないように注意しましょう。

「**()**」というのは実行を表し、この中には引数として、処理に必要な情報を入れます。

### 試してみよう

ブラウザ上でF12キーを押下すると、「開発者ツール」と呼ばれるものが開きます。
開発者ツールにはいくつかタブメニューが備わっていて、「Elements」あるいは「要素」と書かあれているタブを選択すると、**今見ているWebページのHTMLの構造**を見ることができます。

このパネルは一般的に「**インスペクタ**」と呼ばれます。そして、見えているHTMLの構造のことを「DOMツリー」と呼び、`<開始タグ>`と`</終了タグ>`でくくられた1つの塊を「要素」と呼びます。UiPathで取得できるUIがいくつもツリー上に積み重なってWebページはできているのです。

```html
<!-- DOMツリー -->
<div>要素のコンテンツ</div>　<!-- 要素A -->

<div>　<!--  ←　要素Bの開始タグ  -->
    <div>要素のコンテンツ</div>　<!--  ←　要素Bの中の要素C -->
    <div>要素のコンテンツ</div>　<!--  ←　要素Bの中の要素D -->
    <div>要素のコンテンツ</div>　<!--  ←　要素Bの中の要素E -->
</div>　<!--  ←　要素Bの終了タグ  -->
```

この「DOMツリー」から、特定の要素を探し出すことを「**DOM探索**」といいます。難しそうですが、意外と**かなり簡単**です。

さっそくDOM探索開始です。「Console」あるいは「コンソール」と書かれたタブを選択すると、文字が打ち込める画面が表示されます。

そこに次のコードをコピペしてEnterキーを入力してみましょう。

```javascript
document.querySelector('body');
```

コンソールパネルに`body`要素が出力されるはずです。

![](https://qiita-image-store.s3.amazonaws.com/0/142709/9d55970f-3747-e06a-5edb-2f34da6a99f0.png)

Google Chromeなら、

> `<body>...</body>`

などのように出力されている部分にマウスを乗せると、Webページの中のどこにその要素がいるのかがわかります。どうやらWebページのコンテンツすべてをこの`body`要素というのはくくっているようですね。

![](https://qiita-image-store.s3.amazonaws.com/0/142709/a31fb08b-f08d-d64e-6765-df341811c6bf.png)

### もう少し試してみよう

次は画像を探してみましょう。

画像は`img`要素です。次のようなコードで`img`要素を探すことができます。

```javascript
document.querySelector('img');
```

**1つの`img`要素**が探索できていると思います。この例ではQiitaログイン後のページで実行していますが、自分のアイコン画像を表示している`img`要素が探索できているようです。

![](https://qiita-image-store.s3.amazonaws.com/0/142709/4f3c54b9-52b0-c221-d6ff-a4387364bc32.png)

ここで探索できているのは「最初に登場する`img`要素」でしたが、すべての`img`要素を探索したいケースは少なくないでしょう。

そういった場合には次のコードですべての`img`要素を探索することができます。

```javascript
document.querySelectorAll('img');
```

いかがでしょうか？Webブラウザによって表現の仕方は異なりますが、`NodeList`という値が表示されたかと思います。

![](https://qiita-image-store.s3.amazonaws.com/0/142709/6327002f-58c5-61e4-f156-b115beaf7f28.png)

前述の`doucment.querySelector()`で取得できる値は**1つの要素**であり、「**Element node**」あるいは「要素ノード」、文脈によっては単に「要素」や「Node」と言ったりします。

今回の`document.querySelectorAll()`で取得できた値は**複数の要素を持つリスト**であり、**`NodeList`**と呼びます。

この`Node`と`NodeList`という言葉と、それぞれの違いは重要なので覚えておきましょう。


### JavaScriptで利用するセレクタにも触れてみよう

`document.querySelector()`にしろ`document.querySelectorAll()`にしろ、括弧の中には文字列で「セレクタ」を渡して利用します。

「セレクタ」とはJavaScriptで要素を探索するときに使うルールです。UiPathにも「セレクタ」は存在しますが、記法は違えどこれらは感覚的に同じものです。

例えば「submit.gifという画像で表現された送信ボタン」を探索するには、それぞれこのように書きます。

```javascript
/* JavaScript */
var selector = 'input[src="/form/submit.gif"]';
```

```xml
<!-- UiPath -->
<webctrl tag='INPUT' src='/form/submit.gif' />
```

UiPathはアプリケーションをまたいで様々なUIを取得できる点で優れていますが、Webページ内の探索力はセレクタ以外の様々な機能も使えるJavaScriptのほうがかなり柔軟ですし、個人的に安定感もです。

UiPathでもいくつか共通している部分がありますが、JavaScriptのセレクタは次のように記述することができます。

|セレクタ|対象|
|:--|:--|
|`div`|`div`要素|
|`.hoge`|`class`属性に`hoge`を含む要素|
|`.hoge.piyo`|`class`属性に`hoge`且つ`piyo`を含む要素|
|`#hoge`|`id`属性が`hoge`に一致する要素|
|`[id]`|`id`属性を持つ要素|
|`[class="hoge piyo"]`|`class`属性値が`hoge piyo`である要素|
|`div + p`|`div`要素の次にある`p`要素|
|`.hoge[id] > p ~ * span`|`class`属性に`hoge`を持ち、且つ`id`属性を持つ要素の、直下の子要素である`p`要素の後に続くあらゆる要素の子孫要素である`span`要素|

セレクタを記述するときに重要なのは「半角スペース」を意識することです。セレクタにおいて半角スペースを挟むということは「その要素の子孫（下のDOM階層）である」という意味を持ちます。

```javascript
// class属性に「hoge」と「piyo」を含むdiv要素
document.querySelectorAll('div.hoge.piyo');

// div要素の子孫の、class属性に「hoge」を持つ要素の子孫の、class属性に「piyo」を持つ要素
document.querySelectorAll('div .hoge .piyo');
```

このように単純なものは極めて単純ですが、これら以上にもっと様々な細かい条件を指定することができます。より詳しいセレクタについては元々の仕様である[Selectors](https://www.w3.org/TR/selectors/)などをわかりやすく紹介している[HTMLクイックリファレンス](http://www.htmq.com/style/#sel)や、[MDNのCSSセレクター](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Selectors)などを参照してみてください。


## Q. JavaScriptで要素を探索する方法はわかった。それをどう活用するのか？

**A. `id`属性を指定して、UiPathから簡単なセレクタで取得できるようにします。**

UiPathのセレクタは`id`属性による探索が可能です。また、`id`属性は1つのページにつき同じ値は1つまでしか設定することができませんし、通常なら確実に狙った要素を取得することができます。

セレクタも複雑なものを用意せずにただ`id`属性値だけを指定すればいいだけですから、非常にシンプルです。

実際に`id`属性を付与するには次のようなコードを実行します。

```javascript
document.querySelector('img').id = 'hoge';
document.querySelector('img'); // => img#hoge
```

![](https://qiita-image-store.s3.amazonaws.com/0/142709/7fef0f5e-fbf1-f649-5210-f011ac0a351f.png)

無事、`id`属性が付与できたと思います。これをUiPathで取得する場合次のようなセレクタで取得できます。

```xml
<webctrl id='hoge' tag='IMG' />
```

かなりシンプルで分かりやすいセレクタですね。

例えば、状況によって要素が表示されていたりいなかったりする場合、DOMの構造が複雑だと思った要素をうまくUiPathが見つけることができない場合があります。

そういうときには、UiPathのみだと難しいJavaScriptによるDOM探索で`id`をつけてあげることは大変有効です。

最後に、UiPath社によって用意されている[Automation Challenge](http://www.rpachallenge.com/?lang=ja)というWebサイトで`NodeList.prototype.forEach`を利用した`id`属性の付与を試してみましょう。


## Automation Challenge

[Automation Challenge](http://www.rpachallenge.com/?lang=ja)とは…

UiPath社によって用意されているRPA練習用ページです。あるエクセルに入った情報をCMSを模したフォームに投入する練習ができます。

このページでは、ページを読み込みしなおしたり、登録ボタンを押すと要素の並びや`id`属性値、`name`属性値がランダムで切り替わります。

そのため、通常ならば`Anchor Base`（アンカーベース）アクティビティなどを駆使して突破する必要があります。

インスペクタで、フォームの周りを調査してみます。

調べたい要素の上で右クリックし、コンテキストメニューから「検証」「要素を調査」などを選択するか、`Ctrl + Shift + C`キーを押下後、調べたい要素を選択することで要素を調査することができます。

![](https://qiita-image-store.s3.amazonaws.com/0/142709/b4e25ff3-3ae5-2241-c508-5fbc38e2ce15.png)

![](https://qiita-image-store.s3.amazonaws.com/0/142709/30ca1dad-dc88-8a01-d351-2f2fdd48a576.png)


入力項目の周りを見てみると次のような条件が常に満たされていることがわかりました。

- どんな時も、入力したい`input`要素は`div > label ~ input`の構造を保っている
    - 1つの`div`要素の中に、`label`要素と`input`要素が1つずつセットで入っている
    - `label`要素と`input`要素は必ずしも隣り合っておらず、`br`要素が挟まっているケースもある
- `label`要素は常に投入したい`input`の数しか存在しない
- `label`要素内のテキストは、投入したい情報を表す名称と常に等価
- 入力項目数は常に一定
- 入力項目内容は常に不変

以上のことから、以下の条件をJavaScriptで満たすことができればUiPathで複数のアクティビティを駆使するまでもなくごく単純なセレクタで値を投入することができます。

- 情報を投入したい`input`要素は同じDOM階層に存在する`label`要素内のテキストを`id`に持っている

そう、たった1項目です。

それでは早速やってみましょう。

### JavaScriptを書く

実際に書いていく手順を示します。

1. ページ内の`label`要素を**すべて**探索し、取得できたかを確認する
2. `label`要素を1つずつConsoleに出力する
3. `label`要素の内容テキストと親要素をConsoleに出力する
5. `label`要素の親要素の中にいる`input`要素もConsoleに出力する
6. `label`要素の親要素の中にいる`input`要素の`id`属性に`label`要素の内容テキストを設定する


さて、まずは`label`要素を探索します。

```javascript
document.querySelectorAll('label'); // > NodeList(7) [label, label...
```

続いて、UiPathにもある`forEach()`メソッドで1つずつConsoleに出力していきます。

Consoleに出力するには、UiPathでいう`Write Line`（1行を書き込み）アクティビティのようなメソッド「`console.log()`」を利用します。

```javascript
// function (label) の「label」は好きな文字列で大丈夫です。
// ここでは、label要素が入っていることをわかりやすくするために「label」としています。

document.querySelectorAll('label').forEach(function (label) {
    console.log(label); // > <label>...</label>
});
```

続きまして、`label`要素の**内容テキスト**と**親要素**をConsoleに出力します。

内容のテキストは「`textContent`」というプロパティが生えているのでそれを参照します。親要素の取得には「`parentNode`」を参照します。

`console.log()`は、引数をカンマ区切りで複数渡すことで1行に渡しただけ値を出力させられます。

```javascript
document.querySelectorAll('label').forEach(function (label) {
    console.log(label.textContent, label.parentNode);
});
```

上手く動いてますでしょうか？
続きまして、`label`要素の親要素の中にいる`input`要素もConsoleに出力してみます。

すでに取得できている「要素ノード」の中にある要素をさらに探索したい場合は、その要素からさらに「`querySelector()`」あるいは「`querySelectorAll()`」を叩くことができます。

確実に1つしかない場合や、最初の1つだけで構わない場合には`querySelector()`を利用しましょう。

```javascript
document.querySelectorAll('label').forEach(function (label) {
    console.log(label.textContent, label.parentNode);
    console.log(label.parentNode.querySelector('input'));
});
```

どうでしょうか？UIの見え方は異なるかもしれませんが、コンソールはいまこんな感じになっているはずです。

![](https://qiita-image-store.s3.amazonaws.com/0/142709/4fc05ccb-e80c-b0f9-a882-5f7bb0530914.png)

ここまできたら必要な値はすべて用意できていますから、後は`id`属性値を設定するだけですね。

「`label`要素の親要素の中にいる`input`要素の`id`属性に`label`要素の内容テキストを設定する」を実践してみます。

```javascript
// .trim() はおまじないです。
// 文字列の前後についた余計な空白文字を削除してくれる働きがあります

document.querySelectorAll('label').forEach(function (label) {
    label.parentNode.querySelector('input').id = label.textContent.trim();
});
```

![](https://qiita-image-store.s3.amazonaws.com/0/142709/f1ef9d12-5460-796b-11f9-6fb874107749.png)

無事に`id`が設定されたことが確認できたため、これでJavaScriptの処理は以上です。


#### 仕上げ

ここまで触れてきませんでしたが、UiPathの`Inject JS Script`アクティビティで読み込むJavaScriptファイルは、名前のない関数であることが求められます。通常[IIFE](https://developer.mozilla.org/ja/docs/Glossary/IIFE)も渡すことができません。

そのため、次のフォーマットのように記述する必要があります。

※ なお、アクティビティの設定次第では、2つの引数を受け取ることができます
※ return 文を利用すると、UiPathに文字列型の返り値を渡すことができます
※ 詳細な仕様は（[Inject JS Script](https://activities.uipath.com/lang-ja/docs/inject-js-script)）をご覧ください。



```javascript
// テンプレ
function () {
  // your code
};
```

```javascript
// 最終形態
function () {
    document.querySelectorAll('label').forEach(function (label) {
        label.parentNode.querySelector('input').id = label.textContent.trim();
    });
};
```

この状態のコードを、Consoleに入力してテストする場合は次のいずれかの方法で試すことができます。必要に応じて試してみてください。

```javascript
// テスト方法1：コメントアウトする
// function () {
    document.querySelectorAll('label').forEach(function (label) {
        label.parentNode.querySelector('input').id = label.textContent.trim();
    });
// };

```

```javascript
// テスト方法2：名前をつける
var hoge = function () {
    document.querySelectorAll('label').forEach(function (label) {
        label.parentNode.querySelector('input').id = label.textContent.trim();
    });
};

hoge();
```

```javascript
// テスト方法3：IIFEにする
(function () {
    document.querySelectorAll('label').forEach(function (label) {
        label.parentNode.querySelector('input').id = label.textContent.trim();
    });
}());
```

### UiPathで値を入れる

いろいろとアクティビティを用いて、ページを開き、「開始ボタン」を押下するなどをした後に、`Set Text`（テキストを設定）アクティビティで値を設定します。そのアクティビティのセレクタには、次のような値を**手動で**指定します。

```vb
"<webctrl id='" + heading + "' tag='INPUT' />"
```
![](https://qiita-image-store.s3.amazonaws.com/0/142709/69ce47bf-ddbf-d312-9476-740a0547263c.png)

`heading`には、「苗字」「電話番号」などの見出しを入れましょう。
この方法なら確実に対象の要素を発見し、値を設置することが可能です。


## 最後に

ここまでお付き合いいただきありがとうございました。

UiPathだけでは厳しい領域は、今回ご紹介したような属性値のランダム生成以外にもたくさんあります。

非表示になっている要素の中にある特定のノードから値を取得したり、`iframe`要素の中の世界に触ったり、オリジナルの`select`要素のようなものから値を選んだりなどなど、突破するにはJavaScriptが欠かせません。

これを気にぜひJavaScriptに触れてみてください。楽しいですし、何よりできることも大きく増えます。
もちろんJavaScriptは今回ご紹介したようなこと以外にも幅広く活躍してくれる素晴らしい言語ですから、突き詰めていくとかなり沼は深いです。

「UiPathでのWebスクレイピングを効率よく、より確実に実施したい」という要件を満たす範囲で勉強してみたい、という方は[MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript)などで

- JavaScriptの基本構文
- DOM API
- Nodeインターフェース（Node.jsは関係ない）
- セレクタ

あたりを中心にチェックしてみるといいかもしれません。

### 注意事項

- JavaScriptは、動作する環境に依存する言語です
    - 可能であれば、安心安全Google ChromeやFirefoxなどのブラウザを利用しましょう
    - 例えば、今回ご紹介したコードはそのままだと~~悪名高い~~Internet Explorerさんでは動作しません
        - とりあえず`NodeList.prototype.forEach = Array.prototype.forEach;`などと最初の行で書いておけば動作しますが、しっかり対応する場合は`for`文を採用する、polyfillをあらかじめロードするなどの方法をとるべきです
- 突き詰めていくと、ほとんどの作業をJavaScriptに頼って自動化することもできますが、UiPathが持つ強みや採用しているメリットを殺してしまう可能性があります
    - 運用効率と相談して、採用する技術を決定してください
    - 今抱えている自動化のロジックのほとんどがJavaScriptに頼れるのであれば、それはもうNode.jsで全部できるかもしれないです
- 今回書いたJavaScriptは検証文が書かれていません
    - UiPathでアクティビティを組んでいくとき、`if`（条件分岐）アクティビティや`TryCatch`（トライキャッチ）アクティビティで検証処理を挟んでいると思います
    - JavaScriptでも、対象の要素が「存在しているのか」「取得できているのか」「受け取った値は正しいか」などの検証文が必要です
    - 今回の例のような単純な処理ではなくなった場合、JavaScriptがエラーを吐きまくって逆に重荷になってしまうかもしれません
- フォームによっては、`input`要素などが元々もっている`id`属性を変更するとうまく送信できない場合があるため、近くの要素に付与して`AnchorBase`アクティビティを利用するか、もはやJavaScriptで値も入力してしまったほうが早いかもしれません


## おまけ

動くかわかりませんが、今回テストしたxamlの全文です。
本キャプチャで登場していたバージョンのUiPathではなぜかChromeをうまく認識しなかったため、動作確認はFirefoxで行っています。

- Studio 2018.3.2 - 10/30/2018
- Enterprise Edition
- Windows インストーラー
- Microsoft Windows 10 Enterprise 64 ビット
- .NET Framework バージョン 4.7.2

なお、日本語版のAutomation Challengeでは手動でも確認しましたが、正しく値を入れても完全な正解ならないようです（50/70）。

```xaml
<Activity mc:Ignorable="sap sap2010 sads" x:Class="Main" mva:VisualBasic.Settings="{x:Null}" sap2010:WorkflowViewState.IdRef="Main_1"
 xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
 xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities"
 xmlns:s="clr-namespace:System;assembly=mscorlib"
 xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger"
 xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation"
 xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation"
 xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib"
 xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=mscorlib"
 xmlns:sd="clr-namespace:System.Data;assembly=System.Data"
 xmlns:ui="http://schemas.uipath.com/workflow/activities"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <TextExpression.NamespacesForImplementation>
    <sco:Collection x:TypeArguments="x:String">
      <x:String>System.Activities.XamlIntegration</x:String>
      <x:String>System.Activities.Validation</x:String>
      <x:String>System.Activities</x:String>
      <x:String>System.Activities.Statements</x:String>
      <x:String>System.Activities.Expressions</x:String>
      <x:String>Microsoft.VisualBasic</x:String>
      <x:String>Microsoft.VisualBasic.Activities</x:String>
      <x:String>System</x:String>
      <x:String>System.Collections.Generic</x:String>
      <x:String>System.Data</x:String>
      <x:String>System.Diagnostics</x:String>
      <x:String>System.IO</x:String>
      <x:String>System.Linq</x:String>
      <x:String>System.Net.Mail</x:String>
      <x:String>System.Xml</x:String>
      <x:String>System.Xml.Linq</x:String>
      <x:String>System.Windows.Markup</x:String>
      <x:String>System.Drawing</x:String>
      <x:String>UiPath.Core</x:String>
      <x:String>UiPath.Core.Activities</x:String>
      <x:String>System.Xml.Serialization</x:String>
      <x:String>System.Collections</x:String>
    </sco:Collection>
  </TextExpression.NamespacesForImplementation>
  <TextExpression.ReferencesForImplementation>
    <sco:Collection x:TypeArguments="AssemblyReference">
      <AssemblyReference>System.Activities</AssemblyReference>
      <AssemblyReference>Microsoft.VisualBasic</AssemblyReference>
      <AssemblyReference>mscorlib</AssemblyReference>
      <AssemblyReference>System.Data</AssemblyReference>
      <AssemblyReference>System</AssemblyReference>
      <AssemblyReference>System.Core</AssemblyReference>
      <AssemblyReference>System.Xml</AssemblyReference>
      <AssemblyReference>System.Xml.Linq</AssemblyReference>
      <AssemblyReference>PresentationFramework</AssemblyReference>
      <AssemblyReference>WindowsBase</AssemblyReference>
      <AssemblyReference>PresentationCore</AssemblyReference>
      <AssemblyReference>System.Xaml</AssemblyReference>
      <AssemblyReference>System.ServiceModel</AssemblyReference>
      <AssemblyReference>System.ComponentModel.Composition</AssemblyReference>
      <AssemblyReference>System.Drawing</AssemblyReference>
      <AssemblyReference>UiPath.UiAutomation.Activities</AssemblyReference>
      <AssemblyReference>UiPath.System.Activities</AssemblyReference>
      <AssemblyReference>UiPath.Excel</AssemblyReference>
      <AssemblyReference>System.ValueTuple</AssemblyReference>
      <AssemblyReference>UiPath.Mail</AssemblyReference>
      <AssemblyReference>System.Runtime.WindowsRuntime</AssemblyReference>
      <AssemblyReference>UiPath.Studio.Plugin.Workflow</AssemblyReference>
    </sco:Collection>
  </TextExpression.ReferencesForImplementation>
  <Flowchart DisplayName="Main" sap2010:WorkflowViewState.IdRef="Flowchart_1">
    <Flowchart.Variables>
      <Variable x:TypeArguments="x:String" Name="addIdScript" />
      <Variable x:TypeArguments="x:String" Default="D:\rpa-test\challenge_ja.xlsx" Name="PATH_XLSXFILE" />
      <Variable x:TypeArguments="x:String" Default="D:\rpa-test\script\set-id.js" Name="PATH_SETID_SCRIPT" />
      <Variable x:TypeArguments="x:String" Default="http://www.rpachallenge.com/?lang=ja" Name="URL_RPA_CHALLENGE" />
    </Flowchart.Variables>
    <Flowchart.StartNode>
      <FlowStep x:Name="__ReferenceID1" sap2010:WorkflowViewState.IdRef="FlowStep_10">
        <ui:ReadTextFile Content="[addIdScript]" DisplayName="テキストファイルを読み込む" FileName="[PATH_SETID_SCRIPT]" sap2010:WorkflowViewState.IdRef="ReadTextFile_1" />
        <FlowStep.Next>
          <FlowStep x:Name="__ReferenceID0" sap2010:WorkflowViewState.IdRef="FlowStep_9">
            <ui:ExcelApplicationScope Password="{x:Null}" DisplayName="Excel アプリケーションスコープ" sap2010:WorkflowViewState.IdRef="ExcelApplicationScope_2" WorkbookPath="[PATH_XLSXFILE]">
              <ui:ExcelApplicationScope.Body>
                <ActivityAction x:TypeArguments="ui:WorkbookApplication">
                  <ActivityAction.Argument>
                    <DelegateInArgument x:TypeArguments="ui:WorkbookApplication" Name="ExcelWorkbookScope" />
                  </ActivityAction.Argument>
                  <Sequence DisplayName="実行" sap2010:WorkflowViewState.IdRef="Sequence_12">
                    <Sequence.Variables>
                      <Variable x:TypeArguments="sd:DataTable" Name="dataTable" />
                    </Sequence.Variables>
                    <ui:ExcelReadRange AddHeaders="True" DataTable="[dataTable]" DisplayName="範囲を読み込む" sap2010:WorkflowViewState.IdRef="ExcelReadRange_2" SheetName="Sheet1">
                      <ui:ExcelReadRange.Range>
                        <InArgument x:TypeArguments="x:String">
                          <Literal x:TypeArguments="x:String" Value="" />
                        </InArgument>
                      </ui:ExcelReadRange.Range>
                    </ui:ExcelReadRange>
                    <ui:OpenBrowser UiBrowser="{x:Null}" BrowserType="Firefox" DisplayName="ブラウザーを開く" Hidden="False" sap2010:WorkflowViewState.IdRef="OpenBrowser_2" NewSession="True" Private="False" Url="[URL_RPA_CHALLENGE]">
                      <ui:OpenBrowser.Body>
                        <ActivityAction x:TypeArguments="x:Object">
                          <ActivityAction.Argument>
                            <DelegateInArgument x:TypeArguments="x:Object" Name="ContextTarget" />
                          </ActivityAction.Argument>
                          <Sequence DisplayName="Do" sap2010:WorkflowViewState.IdRef="Sequence_10">
                            <Sequence.Variables>
                              <Variable x:TypeArguments="ui:WorkbookApplication" Name="data" />
                              <Variable x:TypeArguments="scg:List(x:String)" Name="headingList" />
                              <Variable x:TypeArguments="x:String" Name="variable1" />
                              <Variable x:TypeArguments="scg:IEnumerable(x:Object)" Name="A1" />
                            </Sequence.Variables>
                            <ui:ExcelReadRow DisplayName="見出しを読み込む" sap2010:WorkflowViewState.IdRef="ExcelReadRow_1" Result="[A1]" SheetName="Sheet1" StartingCell="A1" />
                            <Assign DisplayName="代入" sap2010:WorkflowViewState.IdRef="Assign_7">
                              <Assign.To>
                                <OutArgument x:TypeArguments="scg:List(x:String)">[headingList]</OutArgument>
                              </Assign.To>
                              <Assign.Value>
                                <InArgument x:TypeArguments="scg:List(x:String)">[new list (of string)]</InArgument>
                              </Assign.Value>
                            </Assign>
                            <ui:ForEach x:TypeArguments="x:Object" CurrentIndex="{x:Null}" DisplayName="見出しリストをキャッシュ：繰り返し (コレクションの各要素)" sap2010:WorkflowViewState.IdRef="ForEach`1_2" Values="[A1]">
                              <ui:ForEach.Body>
                                <ActivityAction x:TypeArguments="x:Object">
                                  <ActivityAction.Argument>
                                    <DelegateInArgument x:TypeArguments="x:Object" Name="item" />
                                  </ActivityAction.Argument>
                                  <Sequence DisplayName="Body" sap2010:WorkflowViewState.IdRef="Sequence_16">
                                    <AddToCollection x:TypeArguments="x:String" Collection="[headingList]" DisplayName="コレクションに追加" sap2010:WorkflowViewState.IdRef="AddToCollection`1_2" Item="[item.ToString]" />
                                  </Sequence>
                                </ActivityAction>
                              </ui:ForEach.Body>
                            </ui:ForEach>
                            <ui:Click DelayBefore="{x:Null}" DelayMS="{x:Null}" ClickType="CLICK_SINGLE" DisplayName="クリック 'BUTTON  start'" sap2010:WorkflowViewState.IdRef="Click_2" KeyModifiers="None" MouseButton="BTN_LEFT" SendWindowMessages="False" SimulateClick="False">
                              <ui:Click.CursorPosition>
                                <ui:CursorPosition Position="Center">
                                  <ui:CursorPosition.OffsetX>
                                    <InArgument x:TypeArguments="x:Int32" />
                                  </ui:CursorPosition.OffsetX>
                                  <ui:CursorPosition.OffsetY>
                                    <InArgument x:TypeArguments="x:Int32" />
                                  </ui:CursorPosition.OffsetY>
                                </ui:CursorPosition>
                              </ui:Click.CursorPosition>
                              <ui:Click.Target>
                                <ui:Target ClippingRegion="{x:Null}" Element="{x:Null}" TimeoutMS="{x:Null}" InformativeScreenshot="5ff3665502e8a8cf2fe3a2c09dd5aef8" Selector="&lt;webctrl tag='BUTTON' /&gt;" WaitForReady="INTERACTIVE" />
                              </ui:Click.Target>
                            </ui:Click>
                            <ui:ForEachRow CurrentIndex="{x:Null}" DataTable="[dataTable]" DisplayName="繰り返し (各行)" sap2010:WorkflowViewState.IdRef="ForEachRow_2">
                              <ui:ForEachRow.Body>
                                <ActivityAction x:TypeArguments="sd:DataRow">
                                  <ActivityAction.Argument>
                                    <DelegateInArgument x:TypeArguments="sd:DataRow" Name="row" />
                                  </ActivityAction.Argument>
                                  <Sequence DisplayName="Body" sap2010:WorkflowViewState.IdRef="Sequence_11">
                                    <Sequence.Variables>
                                      <Variable x:TypeArguments="x:Int32" Default="0" Name="i" />
                                      <Variable x:TypeArguments="ui:GenericValue" Name="rowId" />
                                      <Variable x:TypeArguments="x:Int32" Default="0" Name="j" />
                                    </Sequence.Variables>
                                    <Assign DisplayName="代入" sap2010:WorkflowViewState.IdRef="Assign_2">
                                      <Assign.To>
                                        <OutArgument x:TypeArguments="x:Int32">[i]</OutArgument>
                                      </Assign.To>
                                      <Assign.Value>
                                        <InArgument x:TypeArguments="x:Int32">[i + 1]</InArgument>
                                      </Assign.Value>
                                    </Assign>
                                    <Assign DisplayName="代入" sap2010:WorkflowViewState.IdRef="Assign_5">
                                      <Assign.To>
                                        <OutArgument x:TypeArguments="x:Int32">[j]</OutArgument>
                                      </Assign.To>
                                      <Assign.Value>
                                        <InArgument x:TypeArguments="x:Int32">0</InArgument>
                                      </Assign.Value>
                                    </Assign>
                                    <ui:InjectJsScript InputParameter="{x:Null}" ScriptOutput="{x:Null}" DisplayName="JS スクリプトを挿入：id属性の設定" sap2010:WorkflowViewState.IdRef="InjectJsScript_1" ScriptCode="[addIdScript]">
                                      <ui:InjectJsScript.Target>
                                        <ui:Target ClippingRegion="{x:Null}" Element="{x:Null}" Selector="{x:Null}" TimeoutMS="{x:Null}" WaitForReady="INTERACTIVE" />
                                      </ui:InjectJsScript.Target>
                                    </ui:InjectJsScript>
                                    <ui:ForEach x:TypeArguments="x:Object" CurrentIndex="{x:Null}" DisplayName="繰り返し (コレクションの各要素)" sap2010:WorkflowViewState.IdRef="ForEach`1_1" Values="[row.ItemArray]">
                                      <ui:ForEach.Body>
                                        <ActivityAction x:TypeArguments="x:Object">
                                          <ActivityAction.Argument>
                                            <DelegateInArgument x:TypeArguments="x:Object" Name="item" />
                                          </ActivityAction.Argument>
                                          <Sequence DisplayName="Body" sap2010:WorkflowViewState.IdRef="Sequence_14">
                                            <Sequence.Variables>
                                              <Variable x:TypeArguments="ui:GenericValue" Name="heading" />
                                            </Sequence.Variables>
                                            <TryCatch DisplayName="トライキャッチ" sap2010:WorkflowViewState.IdRef="TryCatch_1">
                                              <TryCatch.Try>
                                                <Sequence DisplayName="シーケンス" sap2010:WorkflowViewState.IdRef="Sequence_17">
                                                  <Assign DisplayName="代入" sap2010:WorkflowViewState.IdRef="Assign_6">
                                                    <Assign.To>
                                                      <OutArgument x:TypeArguments="ui:GenericValue">[heading]</OutArgument>
                                                    </Assign.To>
                                                    <Assign.Value>
                                                      <InArgument x:TypeArguments="ui:GenericValue">[headingList(j)]</InArgument>
                                                    </Assign.Value>
                                                  </Assign>
                                                  <Assign DisplayName="代入" sap2010:WorkflowViewState.IdRef="Assign_3">
                                                    <Assign.To>
                                                      <OutArgument x:TypeArguments="x:Int32">[j]</OutArgument>
                                                    </Assign.To>
                                                    <Assign.Value>
                                                      <InArgument x:TypeArguments="x:Int32">[j + 1]</InArgument>
                                                    </Assign.Value>
                                                  </Assign>
                                                  <ui:SetValue ContinueOnError="{x:Null}" DelayAfter="{x:Null}" DelayBefore="{x:Null}" DisplayName="テキストを設定" sap2010:WorkflowViewState.IdRef="SetValue_2" Text="[item.ToString.Trim]">
                                                    <ui:SetValue.Target>
                                                      <ui:Target ClippingRegion="{x:Null}" Element="{x:Null}" TimeoutMS="{x:Null}" Selector="[&quot;&lt;webctrl id='&quot; + heading + &quot;' tag='INPUT' /&gt;&quot;]" WaitForReady="INTERACTIVE" />
                                                    </ui:SetValue.Target>
                                                  </ui:SetValue>
                                                </Sequence>
                                              </TryCatch.Try>
                                              <TryCatch.Catches>
                                                <Catch x:TypeArguments="s:Exception" sap2010:WorkflowViewState.IdRef="Catch`1_1">
                                                  <ActivityAction x:TypeArguments="s:Exception">
                                                    <ActivityAction.Argument>
                                                      <DelegateInArgument x:TypeArguments="s:Exception" Name="exception" />
                                                    </ActivityAction.Argument>
                                                  </ActivityAction>
                                                </Catch>
                                              </TryCatch.Catches>
                                            </TryCatch>
                                          </Sequence>
                                        </ActivityAction>
                                      </ui:ForEach.Body>
                                    </ui:ForEach>
                                    <ui:Click DelayBefore="{x:Null}" DelayMS="{x:Null}" ClickType="CLICK_SINGLE" DisplayName="クリック 'INPUT'" sap2010:WorkflowViewState.IdRef="Click_1" KeyModifiers="None" MouseButton="BTN_LEFT" SendWindowMessages="False" SimulateClick="False">
                                      <ui:Click.CursorPosition>
                                        <ui:CursorPosition Position="Center">
                                          <ui:CursorPosition.OffsetX>
                                            <InArgument x:TypeArguments="x:Int32" />
                                          </ui:CursorPosition.OffsetX>
                                          <ui:CursorPosition.OffsetY>
                                            <InArgument x:TypeArguments="x:Int32" />
                                          </ui:CursorPosition.OffsetY>
                                        </ui:CursorPosition>
                                      </ui:Click.CursorPosition>
                                      <ui:Click.Target>
                                        <ui:Target ClippingRegion="{x:Null}" Element="{x:Null}" TimeoutMS="{x:Null}" InformativeScreenshot="aa459dca34a06e168b0cd9b8de9d96ec" Selector="&lt;webctrl tag='INPUT' type='submit' /&gt;" WaitForReady="INTERACTIVE" />
                                      </ui:Click.Target>
                                    </ui:Click>
                                  </Sequence>
                                </ActivityAction>
                              </ui:ForEachRow.Body>
                            </ui:ForEachRow>
                          </Sequence>
                        </ActivityAction>
                      </ui:OpenBrowser.Body>
                    </ui:OpenBrowser>
                  </Sequence>
                </ActivityAction>
              </ui:ExcelApplicationScope.Body>
            </ui:ExcelApplicationScope>
          </FlowStep>
        </FlowStep.Next>
      </FlowStep>
    </Flowchart.StartNode>
    <x:Reference>__ReferenceID0</x:Reference>
    <x:Reference>__ReferenceID1</x:Reference>
    <sads:DebugSymbol.Symbol>d1JEOlx3b3Jrc1xtc2YtZGV2XGRvY3NcX2RldlxycGFceGFtbFzjg4jjg6njg7Pjgrbjgq/jgrfjg6fjg7Pjg5fjg63jgrvjgrlcTWFpbi54YW1sPUMDhgIPAgEBRjRGUwIBBEc0R1ICAQNINEhaAgECTAlMoAECAU5PDf4BKAIBBUxXTGwCAVFMIkwxAgFPT6MBT7QBAgFMVRP7AR4CAQZZFV8pAgFHYBX6ASYCAQdZRFlRAgFKXBtcSgIBSVmlAVmtAQIBSGDMAWDhAQIBRWYb9wEmAgEIbR1tqwECAUBuHXUmAgE8dh2BASoCATOCAR2QASgCATGRAR32AS0CAQltpAFtqAECAURtfW2DAQIBQm2OAW2WAQIBQXNSc2gCAT9wU3BgAgE9dr4BdsQBAgE6fCN+LgIBNI4BsAGOAdABAgEykQFOkQFbAgEvlwEj8wEuAgEKfSV90wECATWZAVOZAVYCAQybAVObAVYCAQudASWkAS4CASqlASWsAS4CASatASWxATkCASOyASXjATICAQ/kASXyATACAQ19XH1rAgE4fb8BfdABAgE2ogFPogFWAgEtnwFQnwFTAgErqgFPqgFQAgEppwFQpwFTAgEnrQHKAa0B2QECASSyAbkBsgHKAQIBIrgBK+ABNgIBEPABuAHwAeUBAgEOvAEt3wE4AgERvgEx1AE8AgESvwEzxgE8AgEcxwEzzgE8AgEXzwEz0wFBAgETxAFlxAF1AgEfwQFmwQFvAgEdzAFdzAFkAgEayQFeyQFhAgEYzwHQAc8B5gECARTRAY0B0QHZAQIBFg==</sads:DebugSymbol.Symbol>
  </Flowchart>
  <sap2010:WorkflowViewState.ViewStateManager>
    <sap2010:ViewStateManager>
      <sap2010:ViewStateData Id="ReadTextFile_1" sap:VirtualizedContainerService.HintSize="200,51">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="ExcelReadRange_2" sap:VirtualizedContainerService.HintSize="544,59" />
      <sap2010:ViewStateData Id="ExcelReadRow_1" sap:VirtualizedContainerService.HintSize="508,59" />
      <sap2010:ViewStateData Id="Assign_7" sap:VirtualizedContainerService.HintSize="508,60" />
      <sap2010:ViewStateData Id="AddToCollection`1_2" sap:VirtualizedContainerService.HintSize="200,22" />
      <sap2010:ViewStateData Id="Sequence_16" sap:VirtualizedContainerService.HintSize="222,146">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="ForEach`1_2" sap:VirtualizedContainerService.HintSize="508,279" />
      <sap2010:ViewStateData Id="Click_2" sap:VirtualizedContainerService.HintSize="508,106" />
      <sap2010:ViewStateData Id="Assign_2" sap:VirtualizedContainerService.HintSize="456,60" />
      <sap2010:ViewStateData Id="Assign_5" sap:VirtualizedContainerService.HintSize="456,60" />
      <sap2010:ViewStateData Id="InjectJsScript_1" sap:VirtualizedContainerService.HintSize="456,96" />
      <sap2010:ViewStateData Id="Assign_6" sap:VirtualizedContainerService.HintSize="314,60" />
      <sap2010:ViewStateData Id="Assign_3" sap:VirtualizedContainerService.HintSize="314,60" />
      <sap2010:ViewStateData Id="SetValue_2" sap:VirtualizedContainerService.HintSize="314,96" />
      <sap2010:ViewStateData Id="Sequence_17" sap:VirtualizedContainerService.HintSize="336,420">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="Catch`1_1" sap:VirtualizedContainerService.HintSize="404,21">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">False</x:Boolean>
            <x:Boolean x:Key="IsPinned">False</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="TryCatch_1" sap:VirtualizedContainerService.HintSize="418,647">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
            <x:Boolean x:Key="IsPinned">False</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="Sequence_14" sap:VirtualizedContainerService.HintSize="440,771">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="ForEach`1_1" sap:VirtualizedContainerService.HintSize="456,904">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
            <x:Boolean x:Key="IsPinned">False</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="Click_1" sap:VirtualizedContainerService.HintSize="456,106" />
      <sap2010:ViewStateData Id="Sequence_11" sap:VirtualizedContainerService.HintSize="478,1510">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="ForEachRow_2" sap:VirtualizedContainerService.HintSize="508,1657" />
      <sap2010:ViewStateData Id="Sequence_10" sap:VirtualizedContainerService.HintSize="530,2445">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="OpenBrowser_2" sap:VirtualizedContainerService.HintSize="544,2544">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="Sequence_12" sap:VirtualizedContainerService.HintSize="566,2767">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="ExcelApplicationScope_2" sap:VirtualizedContainerService.HintSize="580,2876" />
      <sap2010:ViewStateData Id="FlowStep_9">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <av:Point x:Key="ShapeLocation">260,224.5</av:Point>
            <av:Size x:Key="ShapeSize">200,51</av:Size>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="FlowStep_10">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <av:Point x:Key="ShapeLocation">260,124.5</av:Point>
            <av:Size x:Key="ShapeSize">200,51</av:Size>
            <av:PointCollection x:Key="ConnectorLocation">360,175.5 360,224.5</av:PointCollection>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="Flowchart_1" sap:VirtualizedContainerService.HintSize="614,636">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">False</x:Boolean>
            <av:Point x:Key="ShapeLocation">330,2.5</av:Point>
            <av:Size x:Key="ShapeSize">60,75</av:Size>
            <av:PointCollection x:Key="ConnectorLocation">360,77.5 360,124.5</av:PointCollection>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
      </sap2010:ViewStateData>
      <sap2010:ViewStateData Id="Main_1" sap:VirtualizedContainerService.HintSize="778,716" />
    </sap2010:ViewStateManager>
  </sap2010:WorkflowViewState.ViewStateManager>
</Activity>
```