UiPathサンプル - Webの価格比較サイトから欲しい製品の価格情報の取得を自動化する

※公開情報のみを参照して書いているため、筆者の推論を一部含んでいます。

#シナリオ
HP製のノートPCが欲しいとして、kakaku.comからその価格をCSVファイルに保存するプロセスを紹介します。

Webページから欲しい情報だけを抽出してCSVファイルに保存するコードをプログラミング言語を使って実装していくのは骨の折れる作業ですが、UiPathを使ったRPAだとわずか数分で作成できてしまいます。


#このサンプルで使用する機能
- UiPath Studio
    - **Data Scraping** - Webページのデータを取得するためのパターン認識をします  
<br>
- UiPathアクティビティ
    - **OpenBrowser** - ブラウザを起動し、指定したURLを開きます
    - **Write Csv File** - 取得したデータをCSVファイルで保存します  
<br>
- Windows Workflow
    - **Assign**コントロール  
<br>
- .Net関連
    - System.data.**DataTable** - Selectメソッド、CopyToDataTableメソッドを使います。

#準備
1. サンプルzipファイルをダウンロードします。（筆者が作成したワークフローです）
    https://drive.google.com/open?id=0ByoHD8pH6Am7SHZWR284V0l4TkE  

    zipファイルには以下のファイルが含まれています。これらのファイルを任意の場所に展開します。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/5024cb36-9460-c191-d82e-84aa1ad6f0c5.png">

        - .screenshots - Data Scrapingした際に自動で保存されるスクリーンショット画像ファイルです
        - Main.xaml - ワークフローのXAML実装
        - PriceList.csv - 出力されるCSVファイル
        - project.json - UiPathプロジェクトファイル
<br>
2. UiPathを起動します。Main.xamlを選択し、UiPathで開きます。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/b3f9ae23-aed0-fd91-7166-9e7dc4fb5381.png">  
<br>

3. Flowchartが表示されます。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/472bc285-3f06-f239-5b3d-230109d98e63.png">
<br>
4. SETUP画面からChrome extensionをインストールします。

<img src="https://qiita-image-store.s3.amazonaws.com/0/202992/7108dc87-1dc0-8caf-63c0-8493a6e1f332.png">


#実行と結果
1. Runボタン（またはF5キー)で実行します。  
<img src="https://qiita-image-store.s3.amazonaws.com/0/202992/680681ad-916c-a3ce-e000-9ceb09926d65.png">  
<br>
2. ブラウザが起動し、自動的に価格リストを読み取っていきます。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/6e7294a8-b07d-3866-6978-8e39564062ab.png" width=80%>  
<br>

3. HP製ノートPCの価格リストを含んだPriceList.csvが出力されます。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/f12c9302-7685-e8b3-db18-e75e7e3664aa.png" width=70%>  
    ※11行目がブランクなのは、価格が以下のように非表示であったためです。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/566a66c5-0285-13b0-a97a-10dd07aec284.png" width=13%>


    ビデオで見るとこのような動作イメージになります。
    [![Video](http://img.youtube.com/vi/NOZsBLUz6eo/0.jpg)](http://www.youtube.com/watch?v=NOZsBLUz6eo)


#ワークフロー作成ビデオ
1から自分でworkflowを作るとこのようになります。
[![Video](http://img.youtube.com/vi/LWJ2CNdX7ac/0.jpg)](http://www.youtube.com/watch?v=LWJ2CNdX7ac)


#解説
1. "Open browser"アクティビティ
 - UiPath.Core.Activities.OpenBrowserを使いブラウザを起動します。  
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/9da9886c-7ad0-f9a9-6638-d900db1f0c34.png">

    プロパティを見てみましょう。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/41ba227f-7631-0fc6-b7f1-4aa3981349d7.png">


    ①ContinueOnError
    OpenBrowserアクティビティ処理中にエラーが発生した場合にすぐに処理を止めるか、エラーを無視して先にすすめるかのオプションです。デフォルトは**false**です。
     **true**とした場合、例えばURLがリンク切れになっていたとしてもエラーメッセージを出さずに次のステップに進みます。  
    ②BrowserType
    IE, Firefox, Chromeの中から起動するブラウザを指定します。  
    ③Url
    ブラウザで開くURLを指定します。  
    ④Private
    Private指定の場合はLogを出しません。  
    ⑤Hidden
    ブラウザをアプリケーションから起動するときHiddenモード（ウィンドウの非表示モード）にするかどうかを指定します。  
    ただし、Windowsではこのオプションの有効・無効はアプリケーション側の仕様に委ねられており、Hiddenとしてもウィンドウが非表示にならない場合が多いです。実際、IE, Firefox, Chromeでは有効にならないと思った方がよいです。

    >**Hiddenオプションが効かない実例**
HiddenモードでChromeを起動させるC#コードは以下のようになります。
しかし、ChromeはHiddenモードで起動しません。（他の"Maximized"や"Minimized"オプションは指定通りに動きます）  
<img src="https://qiita-image-store.s3.amazonaws.com/0/202992/0ec9d057-8b5e-2ff9-1cf6-9f43e7572488.png">

    >参照「[MSDN ProcessWindowStyle 列挙型](https://msdn.microsoft.com/ja-jp/library/system.diagnostics.processwindowstyle(v=vs.110).aspx)」

    ⑥NewSession
    新しいブラウザのセッションを開始するかを指定します。
    ただし、Chromeはじめ多くのブラウザで同時に複数のセッションを張ることは許していません。Chromeを例に取れば、同時に複数セッションを作成したい場合はChromeユーザアカウント（Googleアカウントのことではありません）を複数作り、それらを使い分ける必要があります。（例えば、複数のGmailアカウントのメールを同じPCで同時にチェックするような場合はこうします）

    ⑦Private
    ブラウザをPrivateモードで起動するかを指定します。
    Privateモードの場合、閲覧履歴はブラウザに残りません。

    ⑧UiBrowser
    起動したブラウザに対して何か操作したい場合に変数を指定します。
    >**UiBrowserの使用例**
例えば、起動したブラウザを最後に閉じたい場合には以下のようにInvoke Methodアクティビティを使い実現できます。  
まず、UiBrowser変数を指定します。
<img src="https://qiita-image-store.s3.amazonaws.com/0/202992/7c6ef698-92a6-a37f-7cb8-10119afec581.png">  
OpenBrowserアクティビティのプロパティに変数をセットします。
<img src="https://qiita-image-store.s3.amazonaws.com/0/202992/515da2c0-3d3b-855d-02f1-062bbfa7d780.png">  
Invoke Methodを追加し、Closeメソッドを呼びます。
<img src="https://qiita-image-store.s3.amazonaws.com/0/202992/aa160005-86d1-82ca-a1e9-7de39c2d3174.png">  
参照： "Browser"セクション" - https://www.uipath.com/activities-guide
<br>

2. "Data scraping"アクティビティ
 - UiPath Studioで"Data scraping"を行うと、以下のようにWebサイトの情報を読み取るアクティビティが自動で生成されます。  
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/11c7ca3f-93dd-05a4-9368-a6ca4e9fbd22.png">  
<br>
    ダブルクリックすると以下のようになっています。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/0b08bd01-bd6b-3858-0115-f4b9b3c25c81.png">

    ①"Attach Browser"
    読み取り対象とするWebページをUiPath.Core.Activities.BrowserScopeアクティビティで特定します。プロパティを見ると、Selectorは以下のようになっています。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/b9faae11-4838-46ed-565c-41311fa5c368.png">

    今回は単純にapp属性としてChromeブラウザの実行ファイル名が、title属性にWebページのタイトルのみが指定されています。  

    ②"Extract Structured Data 'DIV main'"
    UiPath.Core.Activities.ExtractDataで欲しいWebページ情報を抽出し、String変数"ExtractDataTable"に代入します。

    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/58143552-3567-c85d-2d47-b15e0cc47c68.png">

    (i) ContinueOnError
    "true"とすると実行中にエラーが発生した場合でも途中で止めず、次のステップに進みます。

    (ii) ExtractMetadata
    欲しいHTMLデータを見つけ出すための探索条件をXML形式で指定します。
>**欲しいHTMLデータを見つけ出す**
（ここでの内容はUiPathを使うために必ずしも理解する必要があるわけではありません。また、公式ドキュメントを参照しているわけではなく、筆者の推論を基にしています。）  
    プロパティからXML Editorを開くと以下のようになっています。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/00d23037-d6c8-105e-4066-c7db4b8033b5.png">  
    一方、Webページのソースは以下のようになっています。  
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/d630d134-3459-58f3-0be2-1e526a95d637.png">  
    XML Editorに戻ると、row要素のwebctrlではtagが"div"で且つclassが"rkgContents"であるHTMLの部分をまず指定しています。次に、その子要素のうちtagが"div"で且つclassが"rkgBox noGraph withReview"である部分を1つのrowの単位として認識させる内容となっています。  
    Webページのソースを見ると、1つのノートPC情報を構成する最上位要素が実際にこのclass（"rkgBox noGraph"）になっており、この単位でノートPCの情報がリストとして繰り返されていることがわかります。  
    また、"ProductName"や"Price"についても、次の(iii)でセレクタとして指定したHTMLの最上位要素から段階的に子要素、孫要素、ひ孫要素・・・とたどっていき最終的に目的のプロダクト情報もしくは価格情報に辿りつくための情報が与えられているのがわかります。

    (iii) Selector
    探索するGUI要素（もしくはその親要素）の属性です。  
    Webページのソースを見ると、今回読み取り対象となるHTMLの最上位要素がIDに"main"をもつDIV要素であることがわかります。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/223ddfbf-2f33-a20d-c194-c886c43404af.png">
  
    (iv) DelayBetweenPagesMS
    "次のページ"がロードされるまで待機する時間（ミリ秒単位）です。  
    (v) MaxNumberOfResults
    抽出する最大件数を指定します。0を指定すると全件になります。デフォルトは100です。  
    (vi) NextLinkSelector
    "次のページ"へとナビゲートするリンクやボタンを特定するためのセレクタです。
    プロパティではアンカー要素"a"の値である「次へ」とa要素の親要素であるdiv要素のIDである"main"が指定されています。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/09083895-92e8-13ce-4477-2008c3e8fe5b.png">  
    こちらが実際のWebページの内容です。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/b7684712-bb1c-e60f-45a2-46d8c176a37a.png">  
    parent要素のIDは"main"となっています。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/59ee891a-2d83-9d43-9e05-291572cfd7de.png">  
    (vii) DataTable
    HTMLデータを格納するDataTable型の変数を指定します。
<br>
**3. Assignアクティビティ**
    抽出したHTMLデータから、名前に"HP"が含まれているものだけを取り出します。

        ExtractDataTable = ExtractDataTable.Select("ProductName LIKE '*HP*'").CopyToDataTable()

    "Select"はSystem.data.DataTableのメソッドで、ワイルドカード"*"とLIKE演算子を使うことで「キーワード"HP"が含まれる"ProductName"をもつデータのみ」を戻り値として返します。

    注意が必要なのは、Selectメソッドの戻り値はDataRow型の配列として戻されるので（次のCSVファイルへの出力プロセスで必要な）DataTable型に変換するためにCopyToDataTableメソッドを使用しています。
<br>
4. "Write CSV"アクティビティ
    最後に、データをCSVファイルとして出力します。
    FilePathとして出力先ファイル"PriceList.csv"を、そしてデータが格納されたDataTable型変数の"ExtractDataTable"を指定します。
    <img src="https://qiita-image-store.s3.amazonaws.com/0/202992/209bd84b-a56e-002c-3d19-3b02ba730d2c.png">