TL;DR
- 最近RPAをはじめました
- 要素を探すアクティビティを呼び出す前に
Inject JS Script
で、取りたい要素に任意のid
を付与するJavaScriptを実行しよう - そうすることで必ず要素を発見できる
- DOM APIを勉強することでできることはどんどん幅広くなります
- 最後にxamlファイル全文を貼ってます
はじめに
- 長いです
- UiPathを利用してWebスクレイピング(Webサイトから情報を抽出する)する時のお話です
- Webサイトの中に登場するUIのことを、本記事では要素と呼称します
-
Inject JS Script
(JS スクリプトを挿入)アクティビティを利用します - よいJavaScriptコードを書くことが目的ではなく、簡単に取得したい要素をUiPathに認識させることを目的としています
- RPAビギナーによる記事のため、誤りやより高速で安全な方法がありまいたらご指摘ください
対象読者
- Webスクレイピングをしたいけれど、うまく取りたい要素を安定してUiPathに認識させることが難しい
- UiPathでの「アクティビティ」「変数」「セレクタ」などはわかってきたけれど、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ページはできているのです。
<!-- 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キーを入力してみましょう。
document.querySelector('body');
コンソールパネルにbody
要素が出力されるはずです。
Google Chromeなら、
<body>...</body>
などのように出力されている部分にマウスを乗せると、Webページの中のどこにその要素がいるのかがわかります。どうやらWebページのコンテンツすべてをこのbody
要素というのはくくっているようですね。
もう少し試してみよう
次は画像を探してみましょう。
画像はimg
要素です。次のようなコードでimg
要素を探すことができます。
document.querySelector('img');
1つのimg
要素が探索できていると思います。この例ではQiitaログイン後のページで実行していますが、自分のアイコン画像を表示しているimg
要素が探索できているようです。
ここで探索できているのは「最初に登場するimg
要素」でしたが、すべてのimg
要素を探索したいケースは少なくないでしょう。
そういった場合には次のコードですべてのimg
要素を探索することができます。
document.querySelectorAll('img');
いかがでしょうか?Webブラウザによって表現の仕方は異なりますが、NodeList
という値が表示されたかと思います。
前述のdoucment.querySelector()
で取得できる値は1つの要素であり、「Element node」あるいは「要素ノード」、文脈によっては単に「要素」や「Node」と言ったりします。
今回のdocument.querySelectorAll()
で取得できた値は複数の要素を持つリストであり、**NodeList
**と呼びます。
このNode
とNodeList
という言葉と、それぞれの違いは重要なので覚えておきましょう。
JavaScriptで利用するセレクタにも触れてみよう
document.querySelector()
にしろdocument.querySelectorAll()
にしろ、括弧の中には文字列で「セレクタ」を渡して利用します。
「セレクタ」とはJavaScriptで要素を探索するときに使うルールです。UiPathにも「セレクタ」は存在しますが、記法は違えどこれらは感覚的に同じものです。
例えば「submit.gifという画像で表現された送信ボタン」を探索するには、それぞれこのように書きます。
/* JavaScript */
var selector = 'input[src="/form/submit.gif"]';
<!-- 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階層)である」という意味を持ちます。
// class属性に「hoge」と「piyo」を含むdiv要素
document.querySelectorAll('div.hoge.piyo');
// div要素の子孫の、class属性に「hoge」を持つ要素の子孫の、class属性に「piyo」を持つ要素
document.querySelectorAll('div .hoge .piyo');
このように単純なものは極めて単純ですが、これら以上にもっと様々な細かい条件を指定することができます。より詳しいセレクタについては元々の仕様であるSelectorsなどをわかりやすく紹介しているHTMLクイックリファレンスや、MDNのCSSセレクターなどを参照してみてください。
Q. JavaScriptで要素を探索する方法はわかった。それをどう活用するのか?
A. id
属性を指定して、UiPathから簡単なセレクタで取得できるようにします。
UiPathのセレクタはid
属性による探索が可能です。また、id
属性は1つのページにつき同じ値は1つまでしか設定することができませんし、通常なら確実に狙った要素を取得することができます。
セレクタも複雑なものを用意せずにただid
属性値だけを指定すればいいだけですから、非常にシンプルです。
実際にid
属性を付与するには次のようなコードを実行します。
document.querySelector('img').id = 'hoge';
document.querySelector('img'); // => img#hoge
無事、id
属性が付与できたと思います。これをUiPathで取得する場合次のようなセレクタで取得できます。
<webctrl id='hoge' tag='IMG' />
かなりシンプルで分かりやすいセレクタですね。
例えば、状況によって要素が表示されていたりいなかったりする場合、DOMの構造が複雑だと思った要素をうまくUiPathが見つけることができない場合があります。
そういうときには、UiPathのみだと難しいJavaScriptによるDOM探索でid
をつけてあげることは大変有効です。
最後に、UiPath社によって用意されているAutomation ChallengeというWebサイトでNodeList.prototype.forEach
を利用したid
属性の付与を試してみましょう。
Automation Challenge
UiPath社によって用意されているRPA練習用ページです。あるエクセルに入った情報をCMSを模したフォームに投入する練習ができます。
このページでは、ページを読み込みしなおしたり、登録ボタンを押すと要素の並びやid
属性値、name
属性値がランダムで切り替わります。
そのため、通常ならばAnchor Base
(アンカーベース)アクティビティなどを駆使して突破する必要があります。
インスペクタで、フォームの周りを調査してみます。
調べたい要素の上で右クリックし、コンテキストメニューから「検証」「要素を調査」などを選択するか、Ctrl + Shift + C
キーを押下後、調べたい要素を選択することで要素を調査することができます。
入力項目の周りを見てみると次のような条件が常に満たされていることがわかりました。
- どんな時も、入力したい
input
要素はdiv > label ~ input
の構造を保っている- 1つの
div
要素の中に、label
要素とinput
要素が1つずつセットで入っている -
label
要素とinput
要素は必ずしも隣り合っておらず、br
要素が挟まっているケースもある
- 1つの
-
label
要素は常に投入したいinput
の数しか存在しない -
label
要素内のテキストは、投入したい情報を表す名称と常に等価 - 入力項目数は常に一定
- 入力項目内容は常に不変
以上のことから、以下の条件をJavaScriptで満たすことができればUiPathで複数のアクティビティを駆使するまでもなくごく単純なセレクタで値を投入することができます。
- 情報を投入したい
input
要素は同じDOM階層に存在するlabel
要素内のテキストをid
に持っている
そう、たった1項目です。
それでは早速やってみましょう。
JavaScriptを書く
実際に書いていく手順を示します。
- ページ内の
label
要素をすべて探索し、取得できたかを確認する -
label
要素を1つずつConsoleに出力する -
label
要素の内容テキストと親要素をConsoleに出力する -
label
要素の親要素の中にいるinput
要素もConsoleに出力する -
label
要素の親要素の中にいるinput
要素のid
属性にlabel
要素の内容テキストを設定する
さて、まずはlabel
要素を探索します。
document.querySelectorAll('label'); // > NodeList(7) [label, label...
続いて、UiPathにもあるforEach()
メソッドで1つずつConsoleに出力していきます。
Consoleに出力するには、UiPathでいうWrite Line
(1行を書き込み)アクティビティのようなメソッド「console.log()
」を利用します。
// function (label) の「label」は好きな文字列で大丈夫です。
// ここでは、label要素が入っていることをわかりやすくするために「label」としています。
document.querySelectorAll('label').forEach(function (label) {
console.log(label); // > <label>...</label>
});
続きまして、label
要素の内容テキストと親要素をConsoleに出力します。
内容のテキストは「textContent
」というプロパティが生えているのでそれを参照します。親要素の取得には「parentNode
」を参照します。
console.log()
は、引数をカンマ区切りで複数渡すことで1行に渡しただけ値を出力させられます。
document.querySelectorAll('label').forEach(function (label) {
console.log(label.textContent, label.parentNode);
});
上手く動いてますでしょうか?
続きまして、label
要素の親要素の中にいるinput
要素もConsoleに出力してみます。
すでに取得できている「要素ノード」の中にある要素をさらに探索したい場合は、その要素からさらに「querySelector()
」あるいは「querySelectorAll()
」を叩くことができます。
確実に1つしかない場合や、最初の1つだけで構わない場合にはquerySelector()
を利用しましょう。
document.querySelectorAll('label').forEach(function (label) {
console.log(label.textContent, label.parentNode);
console.log(label.parentNode.querySelector('input'));
});
どうでしょうか?UIの見え方は異なるかもしれませんが、コンソールはいまこんな感じになっているはずです。
ここまできたら必要な値はすべて用意できていますから、後はid
属性値を設定するだけですね。
「label
要素の親要素の中にいるinput
要素のid
属性にlabel
要素の内容テキストを設定する」を実践してみます。
// .trim() はおまじないです。
// 文字列の前後についた余計な空白文字を削除してくれる働きがあります
document.querySelectorAll('label').forEach(function (label) {
label.parentNode.querySelector('input').id = label.textContent.trim();
});
無事にid
が設定されたことが確認できたため、これでJavaScriptの処理は以上です。
仕上げ
ここまで触れてきませんでしたが、UiPathのInject JS Script
アクティビティで読み込むJavaScriptファイルは、名前のない関数であることが求められます。通常IIFEも渡すことができません。
そのため、次のフォーマットのように記述する必要があります。
※ なお、アクティビティの設定次第では、2つの引数を受け取ることができます
※ return 文を利用すると、UiPathに文字列型の返り値を渡すことができます
※ 詳細な仕様は(Inject JS Script)をご覧ください。
// テンプレ
function () {
// your code
};
// 最終形態
function () {
document.querySelectorAll('label').forEach(function (label) {
label.parentNode.querySelector('input').id = label.textContent.trim();
});
};
この状態のコードを、Consoleに入力してテストする場合は次のいずれかの方法で試すことができます。必要に応じて試してみてください。
// テスト方法1:コメントアウトする
// function () {
document.querySelectorAll('label').forEach(function (label) {
label.parentNode.querySelector('input').id = label.textContent.trim();
});
// };
// テスト方法2:名前をつける
var hoge = function () {
document.querySelectorAll('label').forEach(function (label) {
label.parentNode.querySelector('input').id = label.textContent.trim();
});
};
hoge();
// テスト方法3:IIFEにする
(function () {
document.querySelectorAll('label').forEach(function (label) {
label.parentNode.querySelector('input').id = label.textContent.trim();
});
}());
UiPathで値を入れる
いろいろとアクティビティを用いて、ページを開き、「開始ボタン」を押下するなどをした後に、Set Text
(テキストを設定)アクティビティで値を設定します。そのアクティビティのセレクタには、次のような値を手動で指定します。
"<webctrl id='" + heading + "' tag='INPUT' />"
heading
には、「苗字」「電話番号」などの見出しを入れましょう。
この方法なら確実に対象の要素を発見し、値を設置することが可能です。
最後に
ここまでお付き合いいただきありがとうございました。
UiPathだけでは厳しい領域は、今回ご紹介したような属性値のランダム生成以外にもたくさんあります。
非表示になっている要素の中にある特定のノードから値を取得したり、iframe
要素の中の世界に触ったり、オリジナルのselect
要素のようなものから値を選んだりなどなど、突破するにはJavaScriptが欠かせません。
これを気にぜひJavaScriptに触れてみてください。楽しいですし、何よりできることも大きく増えます。
もちろんJavaScriptは今回ご紹介したようなこと以外にも幅広く活躍してくれる素晴らしい言語ですから、突き詰めていくとかなり沼は深いです。
「UiPathでのWebスクレイピングを効率よく、より確実に実施したい」という要件を満たす範囲で勉強してみたい、という方はMDNなどで
- 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がエラーを吐きまくって逆に重荷になってしまうかもしれません
- UiPathでアクティビティを組んでいくとき、
- フォームによっては、
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)。
<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="<webctrl tag='BUTTON' />" 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="["<webctrl id='" + heading + "' tag='INPUT' />"]" 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="<webctrl tag='INPUT' type='submit' />" 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>