3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SeleniumVBA】WebDriver BiDi接続実験「VBAなめんな ― BiDiでここまでやれる」

3
Last updated at Posted at 2025-11-28

(2025/12/31 更新)
SeleniumVBA ver.7.2からWebDriver BiDi利用の前提となるWebSocketUrlの取得が標準で可能となりました。WebDriver BiDi導入にあたっての手順は以下を御覧ください。

【注意事項】FireFoxはCDPが利用できないため機能が制限されます。そのためEdgeもしくはChromeの利用を前提としています。

※SeleniumVBAのバージョンアップ作業が楽になるExcelマクロを以下に掲載しています
https://note.com/sele_chan/n/ne7a15070b3f0

1 はじめに

本家のSeleniumではWebDriver BiDi の開発が現在進行中で、次期Selenium5から正式リリースとのことですが、Chromeではすでに対応済でSelenium4でも利用可能となっています。

WebDriver BiDiはWebSocket通信により双方向通信が可能となり、私が待ち望んでいたイベント検知ができますので、以下のサイトを参考にチャレンジしてみました。

参考記事ではVBAを使ってWebSocket通信によりCDP接続を行っていましたが、これをWebDriver BiDi接続に切り替えて実験したところ期待どおりのものとなり、WebDriver BiDiの実装は技術的に可能です

G66NXrWbkAE-cHq.jpg

2 SeleniumVBAへの実装手順(ver.7.2以降)

(2025/12/31 更新)
SeleniumVBAのプログラム側でWebSocketURLの取得が可能となりましたので、実装手順が簡略化されています。

(1) VBAからWebSocket通信を利用するために必要なWinHttpのAPI関数群やユーザビリティ向上のためのラッパーが格納されたクラスモジュール(先頭にBiDiがついた2つのクラスモジュール)がテストファイルに格納されているので、運用したいSeleniumVBAファイルにインポートします。

無題.png

(2) 標準モジュールのメインルーチンで以下の記述を入れます。以上で完了です。ver.7.2から導入がかなり簡単になりました。

VBA
Dim driver As WebDriver: Set driver = New WebDriver
  With driver
   
  ' Start
  .StartChrome
   
  ' Browser startup settings (for both Chrome and Edge)
  Dim caps As WebCapabilities: Set caps = .CreateCapabilities
  caps.EnableBiDiMode 'WebDriver BiDiが有効になります 
    
  ' Open
  .OpenBrowser caps
  ' WebDriver BiDiを利用するためにWebSocket通信を行います
   Dim bidi As New BiDiCommandWrapper: bidi.ConnectTo .GetWebSocketUrl
  'bidi. と入力するとBiDi専用メソッドが表示されます
   bidi.

(3) VBEのエラートラップは「エラー処理対象外のエラーで中断」とします。

1734250148-6534UwzXkysnJDK7MRjZoWgB.png

3 VBAコード

VBAコードの公開を考えていましたが、堅牢性の確保のためにコード量が想定より多くなってしまいましたので、こちらにテストファイルを置くことにしました。

4 ラッパー機能

クラスモジュールBiDiCommandWrapper内に記述しています。作成目的はVBAの限界まで挑戦し、どこまでPlaywrightに近づけるべくSPA(ReactやVUE)への対応が可能であるか確認するためです。

ロケーターはすべて利便性の高いXPathのみに統一しています(ShadowDOM内検索のみブラウザの制約上の理由からCSSセレクター指定)。

ラッパー作成にあたっては大部分をAI(Gemini3.0)に助けてもらっていて、AIの解読が容易になるようなコードにしています。コード量が1000行を超えてしまいましたので、VBAコードはテストファイルでご確認ください。

主要メソッド紹介

本記事では、WebDriver BiDiプロトコルとCDP(Chrome DevTools Protocol)を融合させたVBA用クラス BiDiCommandWrapper の中から、特に強力で実用的な主要メソッドを紹介します。


📋 主要メソッド一覧

カテゴリ メソッド名 概要(詳細版)
ナビゲーション ExecuteNavigateAndGetStatus URL遷移後にHTTPステータスを確認。通信(Net)と描画(DOM)が止まるまで待機するSPA同期エンジンにより、遷移直後のエラーを抑制。
要素操作 ExecuteClickByXPath 表示域へスクロールし、フォーカス後にクリック。アクション後の通信安定まで自動待機するため、極めて安定した操作が可能。
ExecuteInputValueByXPath execCommandを使用して入力をシミュレート。React等のステートを確実に更新させ、値が反映されない問題を解決。
ExecuteSelectValueByXPath プルダウン操作。Value値だけでなく表示テキストでの指定が可能。選択後にイベントを強制発火させ、表示変化を確実にトリガー。
ExecuteRegisterAutoClickerByXPath CDPを利用してブラウザ内部に「監視員」を常駐。要素が出現した瞬間に自動クリック。画面遷移後のポップアップ等を「零コンマ数秒」で処理。
特殊・階層操作 GetIframeContextIdByUrl iframeをURLで特定。 取得したIDを他のメソッドの引数に渡すだけで、SwitchToなしにフレーム内を直接操作可能。
ExecuteShadowClick 隠蔽されたShadow DOMをCSSセレクタ配列で再帰的に探索して貫通クリック。Web Components構成の最新サイトを攻略。
管理・最適化 ExecuteEnableResourceBlocking 画像や広告などの読み込みを遮断。ネットワーク負荷を最小化し、実行速度を劇的に向上。
ExecuteWebExtensionInstall Chrome拡張機能を即座にインストール。 広告ブロックや独自ツールを動的に組み込み、ブラウザの機能をプログラムから拡張可能。
AI連携 StartDiscoveryLog ブラウザ内部の通信、エラー、DOM変化を詳細に記録。Gemini等のLLMが原因を即座に診断できる「AI-Ready」なログを出力。

🛠 各メソッドの詳細解説

1. ナビゲーション

ExecuteNavigateAndGetStatus(targetUrl, [waitNetworkIdle], [contextId])

指定したURLへ遷移し、HTTPステータスコードに準じた成否(200:成功, 500:失敗等)を返します。内部で「ネットワーク通信」と「DOMの変化」の両方が止まるまで待機するSPA同期エンジンが動作するため、遷移直後の要素未検出エラーを劇的に抑制します。

  • targetUrl (String): 遷移先のURL。
  • waitNetworkIdle (Boolean): 通信とDOMが完全に静止するまで待機するか(デフォルト: True)。
  • contextId (String): 操作対象のタブやフレームを指定する場合に使用。
Dim status As Long
status = bidi.ExecuteNavigateAndGetStatus("[https://example.com/dashboard](https://example.com/dashboard)")
If status = 200 Then Debug.Print "遷移およびSPA描画成功"

2. 要素操作(モダンフレームワーク対応)

ExecuteClickByXPath(xpath, [searchTimeoutMs], [waitNetworkIdle], ..., [contextId])

対象要素を「表示域へ自動スクロール」→「フォーカス」→「クリック」の順で確実に実行します。操作によって発生した非同期通信が終了するまで自動同期待機を行うため、極めて安定した連続操作が可能です。

  • path (String): 対象要素を特定するXPath。
  • searchTimeoutMs (Long): 要素が出現するまでの最大待機時間(ミリ秒)。
  • waitNetworkIdle (Boolean): クリック後の画面更新通信を待つか。
' スクロールと通信待ちを含む確実なクリック
bidi.ExecuteClickByXPath "//button[@id='submit-order']"

ExecuteInputValueByXPath(xpath, valueToSet, [searchTimeoutMs], [waitNetworkIdle], ..., [contextId])

JavaScriptの execCommand('insertText') を内部的に利用することで、人間がキーボード入力したのと同等のイベントを発生させます。ReactやVue等のフレームワークが単なる .value の書き換えを認識しない(内部状態が更新されない)問題を解決します。

' React等のバリデーションを確実に発火させる入力
bidi.ExecuteInputValueByXPath "//input[@id='username']", "vba_user"

ExecuteSelectValueByXPath(xpath, valueOrText, [selectByText], ..., [contextId])

プルダウン(select要素)を操作します。内部のValue値だけでなく、画面に見えているテキストでの項目選択が可能です。選択後に必要なイベントを自動発火させます。

' 表示されているテキスト(2026年03月)で項目を選択
bidi.ExecuteSelectValueByXPath "//select[@name='calselect']", "2026年03月", True

ExecuteRegisterAutoClickerByXPath(xpath, [timeoutMs])

CDPの Page.addScriptToEvaluateOnNewDocument という特殊な機能を利用し、新しいページが読み込まれるたびに実行されるJavaScript(MutationObserver)を登録します。

' Example: Handle a "Save Successful" popup that might appear after any action
bidi.ExecuteRegisterAutoClickerByXPath "//button[text()='Dismiss']"

' Proceed with standard automation without worrying about the popup timing
bidi.ExecuteClickByXPath "//input[@id='save-records']"True

3. 特殊・階層操作(Shadow DOM & iframe)

ExecuteShadowClick(selectorsArray, [searchTimeoutMs], ..., [contextId])

通常のXPath検索では到達できない、Shadow DOM(カプセル化された内部構造)を貫通してクリックします。引数には、ターゲットに到達するまでのCSSセレクタを配列で渡します。

' 登録しておけば、この後の操作で不意に現れる「保存完了」等のダイアログを自動で消去
bidi.ExecuteRegisterAutoClickerByXPath "//button[text()='閉じる']"

' メインの操作に集中できる
bidi.ExecuteClickByXPath "//input[@id='save-btn']"

GetIframeContextIdByUrl(partialUrl, [timeoutMs])

iframeをURLの一部で特定して操作用ID(contextId)を取得します。このIDを他のすべてのメソッドの引数 contextId に渡すだけで、フレーム内を直接操作できます。

Dim conID As String: conID = bidi.GetIframeContextIdByUrl("login_frame")

' IDを指定してフレーム内の要素をクリック
bidi.ExecuteClickByXPath "//button[@id='submit']", , , , , co
### 4. 管理・デバッグ・AI連携
#### ExecuteEnableResourceBlocking(patterns)
画像、広告、トラッキング通信などをブラウザレベルで遮断します。無駄な通信を省くことで、実行速度を劇的に向上させます。

```VBA
' 画像と広告ドメインをブロックして高速化
Dim blockList: blockList = Array("*.png", "*.jpg", "*google-analytics.com*", "*doubleclick*")
bidi.ExecuteEnableResourceBlocking blockList

ExecuteWebExtensionInstall(extensionPath)

指定したパスからChrome拡張機能を現在のブラウザセッションにインストールします。広告ブロック、セキュリティ強化、あるいは特定の業務補助ツールなどを自動化プロセスの中で動的に組み込むことが可能です。

  • extensionPath (String): 拡張機能のファイルパス。
' Chrome拡張機能を有効にするための設定
caps.AddArguments "--remote-debugging-pipe"
caps.AddArguments "--enable-unsafe-extension-debugging"

' Chrome拡張機能を動的にロードして、クリーンな環境でスクレイピングを開始
bidi.ExecuteWebExtensionInstall Environ("LOCALAPPDATA") & "\Google\Chrome\User Data\Default\Extensions\aapbdbdomjkkjkaonfhkkikfgjllcleb\2.0.16_0"

StartDiscoveryLog([excludeImagesAndCss])

ブラウザ内部で発生するイベントを「AIが解読しやすい形式」で記録します。保存したログをGeminiやChatGPTに読み込ませることで、エラー原因の自動診断が可能な「AI-Ready」なデバッグを実現します。

' ログ記録開始(ノイズとなる画像/CSSを除外)
bidi.StartDiscoveryLog excludeImagesAndCss:=True

' (操作実行...)

' ログを保存。これをAIに渡せばデバッグが完了します。
bidi.StopAndSaveDiscoveryLog "C:\temp\discovery_log.txt"

5 起動済ブラウザからの操作

以下のコードにより実験したところ、起動済EdgeからでもWebDriver BiDIを有効にでき、イベントが検知できました。

'=================================================
'リモートデバッグによりログイン状態のブラウザを自動操作する。
'ショートカットにより、すでに立ち上げた状態から始める。
'=================================================
'ショートカットのリンク先は以下に設定
'"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --remote-debugging-port=9222 --user-data-dir="C:\EdgeDebugProfile"
'=================================================   
 Dim caps As SeleniumVBA.WebCapabilities
  
 Set driver = SeleniumVBA.New_WebDriver
 driver.StartEdge
    
 Set caps = driver.CreateCapabilities(initializeFromSettingsFile:=False)
 caps.SetDebuggerAddress "localhost:9222"
  
 'BiDiを有効にする(このプログラムではTrue必須)
 ccaps.EnableBiDiMod
    
 driver.OpenBrowser caps

'(以下つづく)

6 JavaScriptによる丸投げ戦略

BiDiCommandWrapperクラスでは、WebDriver BiDiという最新プロトコルを使いながら、「ネイティブなBiDiコマンド」を連打せずに「script.callFunction(JS実行)」に処理を寄せる戦略をとっています。

その理由は、VBAというシングルスレッド言語の弱点を補い、通信の渋滞(オーバーヘッド)を回避するためです。具体的に、JSに処理を丸投げするメリットを3つの視点で解説します。

(1) 通信回数の劇的な削減(VBAのフリーズ防止)
VBAで自動化を行う際の最大の敵は、通信待ちによる「Excelの応答なし」です。
「要素を探す」「座標を得る」「スクロールする」「クリックする」という4つの工程をBiDiコマンドで行うと、最低4往復の通信が発生します。

一方、VBA側でこれらすべてを盛り込んだJSコードを書き上げると、script.callFunctionのBiDiコマンドを1回送るだけで、ブラウザ側で全工程を完結させ、最後に「成功したよ」という返事をもらうだけで済みます。

(2) データの「翻訳コスト」の削減
WebDriver BiDiが返す生データは、非常に複雑なJSON構造をしています。VBAでこれを一つずつ解析(パース)するのは、CPU負荷が高く、コードも複雑になりがちです。

これを回避するために、JS側で JSON.stringify({status: 'ok', value: isVisible}) のように、VBAが読みやすいシンプルな文字列に加工してから返させています。

その結果VBA側は「ただの文字列」を受け取って、正規表現などでサクッと判定するだけで済むため、処理が圧倒的に軽くなります。

(3) BiDiの「標準機能」にない高度な判定
WebDriver BiDiの標準仕様には「要素が人間に見えているか(Visibility)」や「ネットワークが静止したか(Network Idle)」といった、私たちが自動化で一番欲しい高レベルな判定機能がまだ不十分です。

MutationObserver でDOMの書き換えを監視したり、fetch/XHR をフックして通信状況を把握したりするのは、ブラウザ内部のJSでしかできません。

BiDiはあくまで「ブラウザ内部へJSを送り込むための高速なトンネル」として利用し、実際の高度なロジックはブラウザのネイティブ機能(JS)に任せるのが、現時点で最も安定した戦略なのです。

(4) まとめると
この「JSへの丸投げ戦略」のおかげで、VBA側は通信エラーや複雑な解析に振り回されることなく、安定してブラウザを制御できているわけです。

7 おわりに

VBA経由でのWebDriver BiDiのWebSocket通信に関する記事が見当たらず、kabkabkab様の記事を頼りに右往左往で進めてきたのが実態でしたが、想像した以上にAIが適切なコードを提示してくれて堅牢性のあるコードに仕上がりつつあります。

実験用ファイルでは、クラシックコマンドとの併用を想定していますので、ラッパーはBiDiでなければ対応が困難な機能に絞っていますが、実験した結果、思った以上に機能が実装出来ることが分かり、VBA経由のBiDi利用への展望が開けてきたのは確かだと思います。

最後になりましたが、WebSocket通信の設定にあたり、kabkabkab様の懇切丁寧な解説を交えた記事に大いに助けられました。お礼を申し上げます。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?