Edge & WebDriver を、Selenium を使わずにオプション付きで起動する

<font color="red">**注意！記事中に「Chronium」という表記が出てきますが、正しくは「Chromium」です！
自身の戒めのためにこのまま残しますが、閲覧する方はお間違いないよう、よろしくお願い致します！**</font>
# 前置き
W3C の WebDriver の仕様を確認すると

https://www.w3.org/TR/webdriver/#example-5

なんだか`New Session`コマンドでオプションを指定して起動できそうなのです。
しかしながら、Edge を`--disable-web-security`と`--user-data-dir=C:\Chrome dev session`のオプションを適応して起動したかったのですが、如何せんどのようにリクエストボディを指定すれば良いのか、皆目見当が付きませんでした
そこで、`Python & selenium`が`WebDriver`に投げてるリクエストを取得しようと始めたのが

https://qiita.com/tamarinn_x/items/af61d68ff622a2423114

これだったんですね。
実は、上記のネタは、全てこのための布石だったのです。
ここから得られた、リクエストは下記の通り
```json:Edge をオプション付きで立ち上げるための New Session コマンドのリクエストボディ
{
   "capabilities": {
      "alwaysMatch": {
         "browserName": "MicrosoftEdge",
         "ms:edgeChromium": true,
         "ms:edgeOptions": {
            "args": [ "disable-web-security", "user-data-dir=C:\\Chrome dev session" ],
            "extensions": [  ]
         },
         "platformName": "windows"
      },
      "firstMatch": [ {

      } ]
   },
   "desiredCapabilities": {
      "browserName": "MicrosoftEdge",
      "ms:edgeChromium": true,
      "ms:edgeOptions": {
         "args": [ "disable-web-security", "user-data-dir=C:\\Chrome dev session" ],
         "extensions": [  ]
      },
      "platform": "WINDOWS",
      "version": ""
   }
}
```
この
```json:
         "args": [ "disable-web-security", "user-data-dir=C:\\Chrome dev session" ]
```
が当該部分でしょう。
という訳で、VBA から直接 WebDriver を利用して、オプションを適応した Edge 操作をしてみましょう！
# VBAで実装
まず、WebDriver へのリクエストボディは、JSON で記述しますし、レスポンスもJSONで返ってきますが
文字列のままこれを扱うのは気が狂うので、VBA で使えるパーサーがあると良いです

https://github.com/VBA-tools/VBA-JSON

私は上記の`Set json_ParseObject = New Dictionary`を、片っ端から`Set json_ParseObject = CreateObject("Scripting.Dictionary")`に変えましたが・・・
それから、以下の記事は、非常に役立ちました！おすすめ！

https://qiita.com/uezo/items/66e20b064ffd5f239b9a

WebDriver を VBA で自動更新、って場合は、この記事が便利！

https://qiita.com/yamato1413/items/4b982aee46f28358fd39

で、肝心なオプション付与した Edge の起動ですが
```vb:オプション付きで Edge の New Session を起ち上げ
Public Const DriverFileName As String = "msedgedriver.exe"
Private Const Front_URL As String = "http://localhost:"
Private Const Back_URL As String = "/session"
Private Base_URL As String

'--disable-web-security と --user-data-dir=C:\Chrome dev session オプションを付与してEdgeを起動するサンプルルーチン
Public Sub Test_Sample()
    Dim SessionID As String
    Dim URL As String
    Dim strXPath As String
    Dim ElementID As String

    'WebDriverのポート番号を指定し、BaseURLを生成
    Base_URL = Front_URL & "9515" & Back_URL
    '対象のURLを指定
    URL = "https://www.yahoo.co.jp/" 'Yahoo のサイトを起ち上げます

    'WebDriver実行
    'WebDriver は VBA マクロを実行している Excel ファイルと同じフォルダにある前提です
    CreateObject("WScript.Shell").Run ThisWorkbook.Path & "\" & DriverFileName, 0, False
    'Edgr起動
    SessionID = RunBrowser_and_Get_SessionID(Array("disable-web-security", "user-data-dir=C:\Chrome dev session"))
    'URLに遷移
    Transition_URL_Nav URL, SessionID
    'EdgeWindowの最大化
    Edge_Window_Maximized SessionID
    'Yahoo天気のリンクのエレメントID取得
    strXPath = "//*[@id=""ToolList""]/ul/li[12]/div/a"
    ElementID = Get_ElementID_From_XPath(SessionID, strXPath, , False)
    'Yahoo天気のリンクのクリック
    Crick_ElementID SessionID, ElementID

End Sub

'ブラウザを起動し、セッションIDを得る
Private Function RunBrowser_and_Get_SessionID(Optional ByRef varOptionAry As Variant = Empty) As String
    Dim Params As Object
    Dim SessionID As String
    Dim objAlwDic As Object
    Dim objBrowserOptions As Object
    Dim objOptions As Object
    Dim strOptionAry() As String
    Dim varNullArray() As Variant
    Dim varArray(0 To 0) As Variant
    Dim i As Long

    RunBrowser_and_Get_SessionID = ""
    Set Params = CreateObject("Scripting.Dictionary")
    Set varArray(0) = CreateObject("Scripting.Dictionary")

    If Check_Array(varOptionAry) Then
        ReDim strOptionAry(LBound(varOptionAry) To UBound(varOptionAry))
        For i = LBound(varOptionAry) To UBound(varOptionAry)
            strOptionAry(i) = CStr(varOptionAry(i))
        Next i
        Set objAlwDic = CreateObject("Scripting.Dictionary")
        Set objBrowserOptions = CreateObject("Scripting.Dictionary")
        Set objOptions = CreateObject("Scripting.Dictionary")
        objOptions.Add "args", strOptionAry
        objOptions.Add "extensions", varNullArray
        objBrowserOptions.Add "browserName", "MicrosoftEdge"
        objBrowserOptions.Add "ms:edgeChromium", True
        objBrowserOptions.Add "ms:edgeOptions", objOptions
        objBrowserOptions.Add "platformName", "windows"
        objAlwDic.Add "alwaysMatch", objBrowserOptions
        objAlwDic.Add "firstMatch", varArray
        Params.Add "capabilities", objAlwDic

        Set objBrowserOptions = CreateObject("Scripting.Dictionary")
        objBrowserOptions.Add "browserName", "MicrosoftEdge"
        objBrowserOptions.Add "ms:edgeChromium", True
        objBrowserOptions.Add "ms:edgeOptions", objOptions
        objBrowserOptions.Add "platformName", "WINDOWS"
        objBrowserOptions.Add "version", ""
        Params.Add "desiredCapabilities", objBrowserOptions
        SessionID = SendRequest("POST", Base_URL, Params)("value")("sessionId")
    Else
        Params.Add "desiredCapabilities", CreateObject("Scripting.Dictionary")
        Params.Add "requiredCapabilities", CreateObject("Scripting.Dictionary")
        SessionID = SendRequest("POST", Base_URL, Params)("sessionId")
    End If

    'ブラウザ起動
    Set Params = Nothing
    RunBrowser_and_Get_SessionID = SessionID

End Function

'指定URLに遷移
Private Sub Transition_URL_Nav(ByRef URL As String, ByRef SessionID As String)
    Dim Params As Object

    Set Params = CreateObject("Scripting.Dictionary")
    Params.Add "url", URL
    SendRequest "POST", Base_URL & "/" & SessionID & "/url", Params
    Set Params = Nothing

End Sub

'EdgeWindowの最大化
Private Sub Edge_Window_Maximized(ByRef SessionID As String)
    Dim Params As Object
    Set Params = CreateObject("Scripting.Dictionary")
    SendRequest "POST", Base_URL & "/" & SessionID & "/window/maximize", Params
End Sub

'XPathからElementIDを取得
Private Function Get_ElementID_From_XPath(ByRef SessionID As String, ByRef strXPath As String, Optional ByRef ElementID As String = "", Optional ByRef flgEdge As Boolean = True) As String
    Dim Params As Object
    Dim strElementID As String
    Dim strElem As String

    strElem = "ELEMENT"
    If Not flgEdge Then strElem = "element-6066-11e4-a52e-4f735466cecf"
    Get_ElementID_From_XPath = ""
    Set Params = CreateObject("Scripting.Dictionary")
    Params.Add "using", "xpath"
    Params.Add "value", strXPath
    If Len(ElementID) = 0 Then
        strElementID = SendRequest("POST", Base_URL & "/" & SessionID & "/element", Params)("value")(strElem)
    Else
        strElementID = SendRequest("POST", Base_URL & "/" & SessionID & "/element/" & ElementID & "/element", Params)("value")(strElem)
    End If
    Set Params = Nothing
    Get_ElementID_From_XPath = strElementID

End Function

'指定ElementIDをクリック
Private Sub Crick_ElementID(ByRef SessionID As String, ByRef ElementID As String)
    Dim Params As Object
    Set Params = CreateObject("Scripting.Dictionary")
    SendRequest "POST", Base_URL & "/" & SessionID & "/element/" & ElementID & "/click", Params
End Sub

'これは【Excel VBAでSeleniumBasicを使わずにスクレイピングする】から利用させて頂いてます
'リクエストの送信
Private Function SendRequest(method As String, URL As String, Optional data As Object = Nothing) As Object
    ' クライアントの起動
    Dim client As Object
    Set client = CreateObject("MSXML2.ServerXMLHTTP")

    ' メソッドに応じてリクエスト送信
    client.Open method, URL
    If method = "POST" Or method = "PUT" Then
        client.setRequestHeader "Content-Type", "application/json"
        client.send JsonConverter.ConvertToJson(data)
    Else
        client.send
    End If

    ' 送信完了待ち
    Do While client.readyState < 4
        DoEvents
    Loop

    ' レスポンスをDictionaryに変換してリターン
    Dim Json As Object
    Set Json = JsonConverter.ParseJson(client.responseText)
    Set SendRequest = Json
End Function

'配列化が空か？
Public Function Check_Array(ByRef aryVal As Variant) As Boolean
On Error GoTo Err_Check_Array

    Check_Array = False
    If IsEmpty(aryVal) Then
        Exit Function
    ElseIf Not IsArray(aryVal) Then
        Exit Function
    ElseIf UBound(aryVal) < LBound(aryVal) Then
        Exit Function
    End If
    Check_Array = True

Err_Check_Array:
End Function
```
全然オプションを指定した意味が無いサンプルですが・・・
`RunBrowser_and_Get_SessionID`は引数の起動オプションを指定しないと、Edgeモードで立ち上がります。
オプションを配列で指定すると、Chroniumモードで立ち上がります。
この辺の話は

https://qiita.com/tamarinn_x/items/f771c28a841e0536996f

を参考にしてください。そう、上記記事もこの記事の布石だったのです。
`Get_ElementID_From_XPath`の`flgEdge`引数は、省略や True の場合、Edgeモードに、False の場合は Chroniumモードに対応します。
次は、`actions = ActionChains(driver)`あたりを**seleniumを使わずに**ってネタで行きましょうかね・・・