【Excel】ZeroInstall BrowserDriver for VBAを使用したWebアプリ操作

# はじめに
これは、[Visual Basic Advent Calendar 2023](https://qiita.com/advent-calendar/2023/vb)の22日目の記事となります。

これまで、ExcelでWebスクレイピングをするのに、TinySeleniumVBA および SeleniumVBA(TinySeleniumVBA 機能強化版) を紹介してきました。
TinySeleniumVBA では、WebブラウザのバージョンアップがあるとSelenium WebDriverの更新が必要になります。これについては、yamato1413 さんが WebDriver 自動更新の別途モジュール([WebDriverManager-for-VBA](https://github.com/yamato1413/WebDriverManager-for-VBA))を提供しています。

Playwright や Puppeteer のように DevTools プロトコル対応すれば、Selenium WebDriver 自体が不要となります。誰かVBAでやってないかと検索してみると、昨年既に公開されてました。

https://github.com/24000/ChromeControler-No-Selenium-WebDriver-VBAJSON/tree/master

コメント欄で作者の方と相談して、製品名は製品の特徴である「何もインストールしない」を謳った「ZeroInstall BrowserDriver for VBA」、略して「ZeroInstallBDr」となりました。

https://qiita.com/kabkabkab/items/9952a796ee9244fc98ad

# 導入準備
1.上記の[GitHubのページ](https://github.com/24000/ChromeControler-No-Selenium-WebDriver-VBAJSON/tree/master)から、`<> Code`ボタンをクリックして、「DownLoad　Zip」を選択します。　
2.ダウンロードされた「ChromeControler-No-Selenium-WebDriver-VBAJSON-master.zip」ファイルを展開します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/78f85116-042f-d8d1-d7a5-d2603204f73b.png)

3.ExcelのVBE(Visual Basic Editor)を開きます。
4.展開したファイルをプロジェクト画面にドラッグ&ドロップするだでインポートされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/08585e59-ba77-4de1-5ece-d6634cc9ae16.png)

5.標準モジュールを追加して、コードを記述します。

# 使用方法
現在実装されているメソッドなどは、Wikiに記載されています。Sleepについては、Driver.SleepByWinAPI で実装されています。

https://github.com/24000/ChromeControler-No-Selenium-WebDriver-VBAJSON/wiki

# ZeroInstallBDrでWebアプリ操作
下記記事で使用したサンプルの体脂肪率を計算するサイトで身長と体重をセットして体脂肪率を求めてみます。

https://qiita.com/yaju/items/9d1f189a5d2118a3f4d3

Excelらしくセルに身長と体重をセットして、体脂肪率のセルに値をセットされるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/f7a6659d-53c8-1c9d-7320-0fb5aa37bc1b.png)

## ソースコード
```vb:Example.bas
Option Explicit

Public Sub Main()
    ' Start WebDriver (Edge)
    Dim Driver As IWebDriver
    Set Driver = New EdgeDriver
    
    ' BMI計算サイト
    Driver.OpenURL ("https://keisan.casio.jp/exec/system/1161228728")
    
    ' 身長をセット
    Dim height
    Set height = Driver.FindElementById("var_身長")
    height.SetValue Cells(1, 2).value
    
    ' 体重をセット
    Dim weight
    Set weight = Driver.FindElementById("var_体重")
    
    weight.SetValue Cells(2, 2).value
    
    ' ボタンクリック
    Driver.FindElementById("executebtn").Click
    
    ' 1秒待機
    Driver.SleepByWinAPI 1000
    
    ' BMIをセット
    Dim bmi
    Set bmi = Driver.FindElementById("ans1")
    Cells(3, 2).value = bmi.GetTextContent

    ' Close
    Driver.CloseWindow
End Sub
```
### 補足
~~現状は、"var_身長"といった日本語は認識してくれません。"var_\u8eab\u9577"とUnicode 変換したものを使用することで対応できます。~~
【2023/12/22追記】
早々に修正版を公開して頂き、日本語で認識されるようになりました。ソースコードも修正しておきました。

Unicode 変換ツール

https://www.oh-benri-tools.com/tools/programming/unicode-escape-unescape

## 実行
体脂肪率ボタンをクリックすると、[Keisan 生活や実務に役立つ計算サイト - 体脂肪率](https://keisan.casio.jp/exec/system/1161228728)のサイトが開きます。身長と体重をセットして自動で計算ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/9fb2bf18-123f-5fdc-e14d-c405245d7295.png)

体脂肪率の結果を取得して、体脂肪率セルに値をセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/a4893933-d526-d2d7-92e6-4dbcb11399d3.png)

### 注意点
初回起動時に下記のメッセージが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/66eeeadb-3aa3-e3b3-274b-56a22d5de630.png)

~~Edgeで起動したのにタイトルがChromeのままなのは、作者に報告しておきます。~~
【2023/12/22追記】
早々に修正版を公開して頂き、修正されました。

### イミディエイトウィンドウ
実行するとデバッグ情報としてイミディエイトウィンドウに下記の情報が出力されます。

```
>>>>>>送信内容：{"id": 1,"method": "Target.setDiscoverTargets","params": {"discover": true}}
受信：{"method":"Target.targetCreated","params":{"targetInfo":{"targetId":"30DA9B2A1E155161DD729C771CF5954A","type":"page","title":"\u65b0\u3057\u3044\u30bf\u30d6","url":"edge://newtab/","attached":true,"canAccessOpener":false,"browserContextId":"A633A1A63709DDC83C1E4BFFE155CD8C","pid":3792}}}
受信：{"id":1,"result":{}}

>>>>>>送信内容：{"id": 2,"method": "Target.attachToTarget","params": {"targetId": "30DA9B2A1E155161DD729C771CF5954A"}}
受信：{"method":"Target.attachedToTarget","params":{"sessionId":"E70A8D892FCF42EA409A8EEDEE4AE96B","targetInfo":{"targetId":"30DA9B2A1E155161DD729C771CF5954A","type":"page","title":"\u65b0\u3057\u3044\u30bf\u30d6","url":"edge://newtab/","attached":true,"canAccessOpener":false,"browserContextId":"A633A1A63709DDC83C1E4BFFE155CD8C","pid":3792},"waitingForDebugger":false}}
受信：{"id":2,"result":{"sessionId":"E70A8D892FCF42EA409A8EEDEE4AE96B"}}

>>>>>>送信内容：{"id": 3,"method": "Page.navigate","params": {"url": "https://keisan.casio.jp/exec/system/1161228728"}}
受信：{"id":3,"result":{"frameId":"30DA9B2A1E155161DD729C771CF5954A","loaderId":"D14DF250D7D15A2967137381DBD4A8F8"}}

>>>>>>送信内容：{"id": 4,"method": "Runtime.evaluate","params": {"expression": "document.readyState;","returnByValue": false}}
受信：{"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":"30DA9B2A1E155161DD729C771CF5954A","type":"page","title":"keisan.casio.jp/exec/system/1161228728","url":"https://keisan.casio.jp/exec/system/1161228728","attached":true,"canAccessOpener":false,"browserContextId":"A633A1A63709DDC83C1E4BFFE155CD8C","pid":4440}}}
受信：{"id":4,"result":{"result":{"type":"string","value":"complete"}}}

>>>>>>送信内容：{"id": 5,"method": "Target.setDiscoverTargets","params": {"discover": true}}
受信：{"id":5,"result":{}}

>>>>>>送信内容：{"id": 6,"method": "Page.enable"}
受信：{"id":6,"result":{}}

>>>>>>送信内容：{"id": 7,"method": "Runtime.evaluate","params": {"expression": "document;","returnByValue": false}}
受信：{"id":7,"result":{"result":{"type":"object","subtype":"node","className":"HTMLDocument","description":"#document","objectId":"2503311007000268046.1.1"}}}

>>>>>>送信内容：{"id": 8,"method": "DOM.getDocument","params": {"depth": 1}}
受信：{"id":8,"result":{"root":{"nodeId":1,"backendNodeId":1,"nodeType":9,"nodeName":"#document","localName":"","nodeValue":"","childNodeCount":2,"children":[{"nodeId":2,"parentId":1,"backendNodeId":31,"nodeType":10,"nodeName":"html","localName":"","nodeValue":"","publicId":"","systemId":""},{"nodeId":3,"parentId":1,"backendNodeId":32,"nodeType":1,"nodeName":"HTML","localName":"html","nodeValue":"","childNodeCount":2,"attributes":["lang","ja"],"frameId":"30DA9B2A1E155161DD729C771CF5954A"}],"documentURL":"https://keisan.casio.jp/exec/system/1161228728","baseURL":"https://keisan.casio.jp/exec/system/1161228728","xmlVersion":"","compatibilityMode":"NoQuirksMode"}}}

>>>>>>送信内容：{"id": 9,"method": "Runtime.evaluate","params": {"expression": "document.getElementById('var_\u8eab\u9577');","returnByValue": false}}
受信：{"id":9,"result":{"result":{"type":"object","subtype":"node","className":"HTMLInputElement","description":"input#var_\u8eab\u9577.text","objectId":"2503311007000268046.1.2"}}}

>>>>>>送信内容：{"id": 10,"method": "DOM.describeNode","params": {"objectId":  "2503311007000268046.1.2","depth": 1,"pierce": false}}
受信：{"id":10,"result":{"node":{"nodeId":0,"backendNodeId":33,"nodeType":1,"nodeName":"INPUT","localName":"input","nodeValue":"","childNodeCount":0,"children":[],"attributes":["size","10","autocomplete","off","type","text","name","var_\u8eab\u9577","id","var_\u8eab\u9577","value","","class","text"],"shadowRoots":[{"nodeId":0,"backendNodeId":34,"nodeType":11,"nodeName":"#document-fragment","localName":"","nodeValue":"","childNodeCount":1,"shadowRootType":"user-agent"}]}}}

>>>>>>送信内容：{"id": 11,"method": "DOM.focus","params": {"objectId":  "2503311007000268046.1.2"}}
受信：{"id":11,"result":{}}

>>>>>>送信内容：{"id": 12,"method": "Input.dispatchKeyEvent","params": {"type":  "keyDown","modifiers": 0,"windowsVirtualKeyCode": 13}}
受信：{"id":12,"result":{}}

>>>>>>送信内容：{"id": 13,"method": "Input.dispatchKeyEvent","params": {"type":  "keyUp","modifiers": 0,"windowsVirtualKeyCode": 13}}
受信：{"id":13,"result":{}}

>>>>>>送信内容：{"id": 14,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(value){value = decodeURI(value);this.value = value;}","arguments": [{"value": "180"}],"objectId": "2503311007000268046.1.2"}}
受信：{"id":14,"result":{"result":{"type":"undefined"}}}

>>>>>>送信内容：{"id": 15,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(){this.dispatchEvent(new CustomEvent('input', {bubbles: true}));}","arguments": [{"value": "180"}],"objectId": "2503311007000268046.1.2"}}
受信：{"id":15,"result":{"result":{"type":"undefined"}}}

>>>>>>送信内容：{"id": 16,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(){this.dispatchEvent(new CustomEvent('change', {bubbles: true}));}","arguments": [{"value": "180"}],"objectId": "2503311007000268046.1.2"}}
受信：{"id":16,"result":{"result":{"type":"undefined"}}}

>>>>>>送信内容：{"id": 17,"method": "Runtime.evaluate","params": {"expression": "document.getElementById('var_\u4f53\u91cd');","returnByValue": false}}
受信：{"id":17,"result":{"result":{"type":"object","subtype":"node","className":"HTMLInputElement","description":"input#var_\u4f53\u91cd.text","objectId":"2503311007000268046.1.3"}}}

>>>>>>送信内容：{"id": 18,"method": "DOM.describeNode","params": {"objectId":  "2503311007000268046.1.3","depth": 1,"pierce": false}}
受信：{"id":18,"result":{"node":{"nodeId":0,"backendNodeId":35,"nodeType":1,"nodeName":"INPUT","localName":"input","nodeValue":"","childNodeCount":0,"children":[],"attributes":["size","10","autocomplete","off","type","text","name","var_\u4f53\u91cd","id","var_\u4f53\u91cd","value","","class","text"],"shadowRoots":[{"nodeId":0,"backendNodeId":36,"nodeType":11,"nodeName":"#document-fragment","localName":"","nodeValue":"","childNodeCount":1,"shadowRootType":"user-agent"}]}}}

>>>>>>送信内容：{"id": 19,"method": "DOM.focus","params": {"objectId":  "2503311007000268046.1.3"}}
受信：{"id":19,"result":{}}

>>>>>>送信内容：{"id": 20,"method": "Input.dispatchKeyEvent","params": {"type":  "keyDown","modifiers": 0,"windowsVirtualKeyCode": 13}}
受信：{"id":20,"result":{}}

>>>>>>送信内容：{"id": 21,"method": "Input.dispatchKeyEvent","params": {"type":  "keyUp","modifiers": 0,"windowsVirtualKeyCode": 13}}
受信：{"id":21,"result":{}}

>>>>>>送信内容：{"id": 22,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(value){value = decodeURI(value);this.value = value;}","arguments": [{"value": "54"}],"objectId": "2503311007000268046.1.3"}}
受信：{"id":22,"result":{"result":{"type":"undefined"}}}

>>>>>>送信内容：{"id": 23,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(){this.dispatchEvent(new CustomEvent('input', {bubbles: true}));}","arguments": [{"value": "54"}],"objectId": "2503311007000268046.1.3"}}
受信：{"id":23,"result":{"result":{"type":"undefined"}}}

>>>>>>送信内容：{"id": 24,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(){this.dispatchEvent(new CustomEvent('change', {bubbles: true}));}","arguments": [{"value": "54"}],"objectId": "2503311007000268046.1.3"}}
受信：{"id":24,"result":{"result":{"type":"undefined"}}}

>>>>>>送信内容：{"id": 25,"method": "Runtime.evaluate","params": {"expression": "document.getElementById('executebtn');","returnByValue": false}}
受信：{"id":25,"result":{"result":{"type":"object","subtype":"node","className":"HTMLInputElement","description":"input#executebtn.bigbutton","objectId":"2503311007000268046.1.4"}}}

>>>>>>送信内容：{"id": 26,"method": "DOM.describeNode","params": {"objectId":  "2503311007000268046.1.4","depth": 1,"pierce": false}}
受信：{"id":26,"result":{"node":{"nodeId":0,"backendNodeId":37,"nodeType":1,"nodeName":"INPUT","localName":"input","nodeValue":"","childNodeCount":0,"children":[],"attributes":["type","button","value"," \u8a08  \u7b97 ","onclick","return checkText()","class","bigbutton","id","executebtn"],"shadowRoots":[{"nodeId":0,"backendNodeId":38,"nodeType":11,"nodeName":"#document-fragment","localName":"","nodeValue":"","childNodeCount":1,"shadowRootType":"user-agent"}]}}}

>>>>>>送信内容：{"id": 27,"method": "DOM.focus","params": {"objectId":  "2503311007000268046.1.4"}}
受信：{"id":27,"result":{}}

>>>>>>送信内容：{"id": 28,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(){this.click();}","arguments": [{"value": ""}],"objectId": "2503311007000268046.1.4"}}
受信：{"id":28,"result":{"result":{"type":"undefined"}}}

>>>>>>送信内容：{"id": 29,"method": "Page.enable"}
受信：{"method":"Page.frameScheduledNavigation","params":{"frameId":"30DA9B2A1E155161DD729C771CF5954A","delay":0,"reason":"formSubmissionPost","url":"https://keisan.casio.jp/exec/system/1161228728"}}
受信：{"method":"Page.frameRequestedNavigation","params":{"frameId":"30DA9B2A1E155161DD729C771CF5954A","reason":"formSubmissionPost","url":"https://keisan.casio.jp/exec/system/1161228728","disposition":"currentTab"}}
受信：{"method":"Page.frameStartedLoading","params":{"frameId":"30DA9B2A1E155161DD729C771CF5954A"}}
受信：{"method":"Page.frameClearedScheduledNavigation","params":{"frameId":"30DA9B2A1E155161DD729C771CF5954A"}}
受信：{"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":"30DA9B2A1E155161DD729C771CF5954A","type":"page","title":"keisan.casio.jp/exec/system/1161228728","url":"https://keisan.casio.jp/exec/system/1161228728","attached":true,"canAccessOpener":false,"browserContextId":"A633A1A63709DDC83C1E4BFFE155CD8C","pid":4440}}}
受信：{"method":"Page.frameNavigated","params":{"frame":{"id":"30DA9B2A1E155161DD729C771CF5954A","loaderId":"C2442CDBFD2FE4FF70259A5C499CEC94","url":"https://keisan.casio.jp/exec/system/1161228728","domainAndRegistry":"casio.jp","securityOrigin":"https://keisan.casio.jp","mimeType":"text/html","adFrameStatus":{"adFrameType":"none"},"secureContextType":"Secure","crossOriginIsolatedContextType":"NotIsolated","gatedAPIFeatures":[]},"type":"Navigation"}}
受信：{"method":"DOM.documentUpdated","params":{}}
受信：{"id":29,"result":{}}

>>>>>>送信内容：{"id": 30,"method": "DOM.enable"}
受信：{"method":"DOM.documentUpdated","params":{}}
受信：{"method":"Page.domContentEventFired","params":{"timestamp":109214.263145}}
受信：{"id":30,"result":{}}

>>>>>>送信内容：{"id": 31,"method": "Runtime.evaluate","params": {"expression": "document.getElementById('ans1');","returnByValue": false}}
受信：{"method":"Page.loadEventFired","params":{"timestamp":109214.390639}}
受信：{"method":"Page.frameStoppedLoading","params":{"frameId":"30DA9B2A1E155161DD729C771CF5954A"}}
受信：{"id":31,"result":{"result":{"type":"object","subtype":"node","className":"HTMLDivElement","description":"div#ans1.answaku","objectId":"2503311007000268046.2.1"}}}

>>>>>>送信内容：{"id": 32,"method": "DOM.describeNode","params": {"objectId":  "2503311007000268046.2.1","depth": 1,"pierce": false}}
受信：{"id":32,"result":{"node":{"nodeId":0,"backendNodeId":77,"nodeType":1,"nodeName":"DIV","localName":"div","nodeValue":"","childNodeCount":1,"children":[{"nodeId":0,"parentId":0,"backendNodeId":78,"nodeType":3,"nodeName":"#text","localName":"","nodeValue":"33.4271"}],"attributes":["class","answaku","id","ans1"]}}}

>>>>>>送信内容：{"id": 33,"method": "Runtime.callFunctionOn","params": {"functionDeclaration": "function(){return this.textContent.replace(/\\s/g, ' ');}","arguments": [{"value": ""}],"objectId": "2503311007000268046.2.1"}}
受信：{"id":33,"result":{"result":{"type":"string","value":"33.4271"}}}

>>>>>>送信内容：{"id": 34,"method": "Browser.close"}
受信：{"id":34,"result":{}}
```

# 最後に
IE11サポート終了問題は、昨年の6月に終わりました。
会社によってはセキュリティーに厳しく「Selenium」と「WebDriver」の使用を禁止しているところもあるようです。そういう会社でもExcelだけは、プログラムが出来る唯一の希望だったりします。

ZeroInstallBDr作者のkabkabkabさんも、会社で「Selenium」と「WebDriver」の使用申請をしたところ却下され、しばらく放置していたがIEのサポート終了が迫ってきたこともあって、「CDPについて調べて自分で作ってみよう！」と思われて開発を始めたようです。
その後、会社でSelenium、WebDriverインストールOKな状況に変わったとのこと。

https://qiita.com/kabkabkab/items/d187fd1622fede038cce

ExcelでWebスクレイピングする用途で、そこまで複雑なことは求められていないと思います。今回のサンプルのようにExcelと連携することで便利に使える利点もあります。

# その他
開いているブラウザを操作することは、「ZeroInstallBDr」では対応していません。
下記サイトでは、パイプを使用して実現させているようです。

https://www.codeproject.com/Tips/5307593/Automate-Chrome-Edge-using-VBA

https://github.com/longvh211/Chromium-Automation-with-CDP-for-VBA