既存RPA資産を活かしてIEで動かしていたアプリをMicrosoft EdgeのIEモードで動かす

## 概要
Microsoft EdgeのIEモードで動かす場合に
AddonsのSelenium IDEで画面操作できるようにしてみました。
ただし、IEで動かしていたRPAを実施していた際の
VBAを既存資産として可能な限り流用することを条件としています。

## この記事を書いた経緯
[Internet Explorer11が2022年6月15日にサポート対象外となる](https://docs.microsoft.com/ja-jp/lifecycle/faq/internet-explorer-microsoft-edge)中、
IEのみで稼動するシステムが少なからず有るようです。
そこでMicrosoft EdgeのIEモードでどうにかしようとする動きも有ります。
[IEモードも少なくとも2029年までは使える](https://docs.microsoft.com/ja-jp/lifecycle/faq/internet-explorer-microsoft-edge#:~:text=Microsoft%20%E3%81%AF%E3%80%81%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%95%E3%82%8C%E3%81%A6,%E3%82%92%E7%B4%84%E6%9D%9F%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82)からだと思います。
とはいえ、いつまでもRPA既存資産(本記事ではExcel VBA)に
頼っていられないのではないでしょうか。
IEをVBAで動かす、といえば[SeleniumBasic](https://github.com/florentbr/SeleniumBasic)が代表的なようですが、
2016年から更新が止まっています。
~~というか、IEとか辞めませんか~~

脱IEの対策を既存資産を活かしつつどのように対応できるか
確かめた記録をこの記事に残すことにしました。

## 検証環境
RPA資産 : Excel 365のVBA(サンプル)
RPA対象 : [Yahoo! JAPAN](https://www.yahoo.co.jp/)

### 注意事項 
記載しているVBAのコードは筆者の環境に
MS Excelが無いため、挙動を確認できていません。
またSeleniumVBAを使ったVBAでもありません。
あくまで想定した挙動のサンプルVBAを
「IEを動かしていたVBAをSelenium IDEの設定ファイルに変換する」
ことにフォーカスしようとしています。

## 既存RPA資産となるExcelマクロ
A列はYahoo! JAPANで検索する文字列、
B列にその検索結果件数を記録、
当時のブラウザはInternet Explorer 11。
処理前後のイメージは次の通り。

<details><summary>before</summary>
![before.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533981/b02c92f5-7ec8-95eb-9259-c8435ba98bcf.png)
</details>

<details><summary>after</summary>
![after.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533981/e8a72cb3-70a7-d40c-0b22-3ff69dbdb807.png)
</details>

<details><summary>VBAのコード(code.txtに抽出)</summary>

```vb:検索結果記録処理
Option Explicit

Sub search()
    Dim objIE As Object, inputTag As Variant, buttonTag As Variant, _
    keywords As Variant, results As Variant, _
    i As Integer, size As Integer, _
    sheet As Object, range As Object, reg As RegExp, matches As Variant
    Dim pattern As String
    pattern = "(\d{1,3})?(,\d{3})*"
    Set reg = new RegExp
    With reg
        .Global = True
        .IgnoreCase = True
        .Pattern = pattern
    End reg

On Error GoTo Finally

    Set sheet = ThisWorkbook.Sheets(1)
    Set range = sheet.Range("A1").End(xlUp)
    size = range.Rows.Count
    Redim keywords(1 to size, 1 to 1)
    Redim results(1 to size, 1 to 1)
    keywords = range

    Set objIE = CreateObject("InternetExplorer.Application")
    objIE.Visible = True
    objIE.Navigate2("https://www.yahoo.co.jp/")
    Call wait(objIE)

    inputTag = objIE.Document.getElementsByName("p")(0)
    buttonTag = objIE.Document.getElementsByTagName("button")(0)
    for i = 1 to size
        inputTag.Value = keywords(i, 1)
        buttonTag.Click
        Call wait(objIE)
        matches = reg.Match(objIE.Document.getElementsByClassName("Hits__item")(0).Value)
        results(i, 1) = matches(0).Value
    next i
    
    sheet.Range("B1:B" + CStr(size)) = results
    objIE.Quit

Finally:

    Set objIE = Nothing

End Sub

Function wait(ByRef objIE As Object, Optional ByVal second As Integer = 5)
    Do While objIE.Busy = True Or objIE.readyState <> READYSTATE_COMPLETE
        DoEvents
    Loop
    Call waitFor(second)
End Function

Function waitFor(ByVal second As Integer)
    Dim future As Date
    future = DateAdd("s", second, Now)
    While Now < future
        DoEvents
    Wend
End Function
```
</details>

## どのように資産を活かすか
- VBA -> MS EdgeのAddons「[Selenium IDE](https://microsoftedge.microsoft.com/addons/detail/selenium-ide/ajdpfmkffanmkhejnopjppegokpogffp?hl=ja-JP)」のsideファイルに可能な限り変換
- VBAのインプット/アウトプット -> Selenium IDEの入出力に変換(割愛)

## VBA -> Selenium IDEに変換(可能な限り)
[InternetExplorerオブジェクトの持つメソッド](https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa752084(v=vs.85))と
[InternetExplorer.Documentオブジェクトの持つメソッド一欄](https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa752052(v=vs.85)#property-values)として以下3記事をそれぞれピックアップ
[IHTMLDocument interface](https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa752641(v=vs.85))
[_win32_IHTMLDocument2 interface](https://docs.microsoft.com/en-us/previous-versions//hh776919(v=vs.85)?redirectedfrom=MSDN)
[IHTMLDocument3_Interface interface](https://docs.microsoft.com/en-us/previous-versions//hh773775(v=vs.85)?redirectedfrom=MSDN)
これにClickとSubmitを加えたメソッド名一欄を用意しました。
その一欄でVBAのコードをgrepとsedで画面操作を一部ですが抽出しました。

<details><summary>Selenium IDEのコマンド、パラメータのcsvに変換</summary>

```bash:
sed -n 3,59p code.txt | grep -w -f pattern.txt | sed 's/^ *//g' | sed -E "s/^([a-zA-Z0-9 \=]{1,}[\.]){1,}|\(0\)|(\"\).*)$//g" | sed -f sed_pattern.txt | sed -E s/[\(]{1}[\"]{1}/,/g > result.csv
```

</details>

<details><summary>pattern.txt</summary>

```txt:pattern.txt
ExecWB
GetProperty
GoBack
GoForward
GoHome
GoSearch
Navigate
Navigate2
PutProperty
QueryStatusWB
Refresh
Stop
AttachEvent
Clear
Click
Close
CreateDocumentFragment
CreateElement
CreateStyleSheet
CreateTextNode
DetachEvent
getElementById
getElementsByName
getElementsByClassName
getElementsByTagName
Open
Recalc
Submit
Write
Writeln
```
</details>

<details><summary>sed_pattern.txt</summary>

```bash:sed_pattern.txt
s/Navigate2/open/g
s/Click/click/g
s/getElementById/store text/g
s/getElementsByName/store text\,name/g
s/getElementsByClassName/store text\,css/g
s/getElementsByTagName/store text\,tag/g
```

</details>

<details><summary>result.csv</summary>

```text:result.csv
open,https://www.yahoo.co.jp/
store text:name,p
store text:tag,button
click
store text:css,Hits__item
```

</details>

csvに抽出したコマンド、パラメータは
下記のsideファイル(Selenium IDEの設定ファイル)のように

(ルート) -> commands -> tests -> 1列目をCsvValueのcommand, 2列目をvalueに設定

これらに設定してtests,suitesを作成するようにします。

<details><summary>空のsideファイル</summary>

```json:空のsideファイル
{
  "id": "bf97d162-8455-469e-b827-2dc9ce77c2a9",
  "version": "2.0",
  "name": "blank",
  "url": "",
  "tests": [{
    "id": "8c4010ed-fa49-41c5-ba15-c0a3580f3516" ,
    "name": "template",
    "commands": [{
      "id": "ed4ba57d-3498-4af5-ab13-6253abf83e69",
      "comment": "",
      "command": "open",
      "target": "",
      "targets": [],
      "value": ""
    }]
  }],
  "suites": [{
    "id": "fbc33c96-414b-425e-932e-96da7372c9e3",
    "name": "Default Suite",
    "persistSession": false,
    "parallel": false,
    "timeout": 300,
    "tests": ["8c4010ed-fa49-41c5-ba15-c0a3580f3516"]
  }],
  "urls": [],
  "plugins": []
}
```

</details>
sideファイルを作る際の注意点は
suitesのtestsの値は
ルートにあるtestsのidの値にすることです。

あとは先程出力したresult.csvを上記のjsonに当てはめるように
JavaのJacksonでcsvファイルを取り込み、出力しました。

(2022/3/29更新)Jacksonでのプログラム説明が長くなったため、
[別記事](https://qiita.com/harayoshi/items/9ad132c6740d26471304)にしました。

</details>

<details><summary>出力されたjson</summary>

```json:出力されたjson
{
  "id" : "c462be82-6000-417c-9cdb-5fb2707ad887",
  "version" : "2.0",
  "name" : "",
  "url" : "",
  "tests" : [
    {
      "id" : "36cd8244-7a5c-47c7-8a80-a9344418f52f",
      "comment" : "",
      "commands" : [
        {
          "id" : "2f7b0829-1c2a-4e0e-bdba-1e0a83c0e562",
          "comment" : "",
          "command" : "open",
          "target" : "",
          "targets" : [ ],
          "value" : "https://www.yahoo.co.jp/"
        },
        {
          "id" : "4de0018d-1e55-4750-991f-8db3dcb7751b",
          "comment" : "",
          "command" : "store text:name",
          "target" : "",
          "targets" : [ ],
          "value" : "p"
        },
        {
          "id" : "f5b049be-333d-4004-8de4-866af0799efe",
          "comment" : "",
          "command" : "store text:tag",
          "target" : "",
          "targets" : [ ],
          "value" : "button"
        },
        {
          "id" : "7d9f7c1d-181a-4edb-bd26-cccdd258d0c7",
          "comment" : "",
          "command" : "click",
          "target" : "",
          "targets" : [ ],
          "value" : null
        },
        {
          "id" : "30b373fd-1b12-440d-bd21-2ffad7f50ee2",
          "comment" : "",
          "command" : "store text:css",
          "target" : "",
          "targets" : [ ],
          "value" : "Hits__item"
        }
      ]
    }
  ],
  "suites" : [
    {
      "id" : "971de2bd-01ec-4819-94d0-51030d2f250e",
      "name" : "no name",
      "persistSession" : false,
      "parallel" : false,
      "timeout" : 300,
      "tests" : [
        "36cd8244-7a5c-47c7-8a80-a9344418f52f"
      ]
    }
  ]
}
```

</details>

この状態ではそのまま実行はできませんが、
多少の訂正で済む程度にまではできたと思います。

### 最後に
諸々折り畳んで書いていったものの、長文になりました。
あと、なんだかラノベみたいなタイトルになってしまいました。
最後までお読みいただき、ありがとうございました。