1
0

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】シャドウルート内の要素をクリックする

Last updated at Posted at 2025-01-11

1 はじめに

少し前になりますが、TinySeleniumVBAユーザーから「どのような手段を尽くしても要素が取得できない。どうしたらよいか」との趣旨による以下の質問がありました。
確認したところ、案の定取得したい要素がシャドウルート内にあったので、機能拡張版のSeleniumVBAの導入を推奨しました。

SeleniumVBAは、2021年11月のSeleniumアップデートでShadow DOM内の要素検索が便利になった(JavaScriptが必須でなくなりFindElementが使用可能になった)ことに対応しています。

2 やりたいこと

ServiceNow様トップページ右上にある「Sign In」の箇所(以下画像の赤枠)をクリックすることを目標とします。
「なんだクリックするだけか」と思われるかもしれませんが、通常の要素検索の方法では「no such element」のエラーが出て要素取得できません

タイトルなし2.png

ログイン画面を出すだけで、実際にログインする訳ではありませんので、どなたでもご確認いただけます。

3 事前準備

(1)シャドウルートの判定

ServiceNow様トップページを開き「Sign In」を右クリックし「検証」からデベロッパーツールで「完全なXPath」をコピーしたところ、以下のとおりになりました。

操作はChromeで行っていますが、Edgeでも同様に確認できます。

【Sign In までの完全なXPath】
/html/body/dps-app//div/header/dps-navigation-header//header/div/div[2]/ul/li[2]/dps-login//div/dps-button//button

パス内に「//」が存在すればシャドウルートと判定します。「//」の左側「dps-app」「dps-navigation-header」「dps-login」がシャドウルート入口です。

結果からお伝えすると「dps-button」はクリックできなかったので対象外としました。試行のうえ上位要素の「dps-login」がクリックできる要素でした。

(2)構造確認

次に、Chromeのソース画面を見ますと、クリックしたい要素が一番下の四角囲み部分となり、その上位層をみると、シャドウルートであることが分かる四角囲みの「#shadow-root(open)」が複数表示されていることが分かります。

タイトルなし.png

#shadow-rootが表示されない場合は、デベロッパーツール上部の歯車ボタンを押し、PreferencesタブのElementsの「Show user agent shadow DOM」の選択肢にチェックを入れます。Edge利用の場合も同様です。

4 ExcelVBAコード

以下のコードを丸ごとコピーして、SeleniumVBA.xlsmに新規の標準モジュールを作成して貼り付けますと実際に動作確認ができます。

もし、SeleniumVBAがまったく初めての方は、こちらの記事を参考に新規の標準モジュール作成まで行ってください。

このコードは「Sign In」クリック後のログイン画面で止まるようにしてあります。ブラウザバックして、元の画面になれば正常に動作しています。

VBA
Sub test_shadowroot2()
   Dim driver As SeleniumVBA.WebDriver
   Dim caps   As SeleniumVBA.WebCapabilities
   Set driver = SeleniumVBA.New_WebDriver
   '============================================
   '【事例】Sign Inのリンクをクリックしたい
   '完全なXpathをコピーすると以下のとおりとなった
   '/html/body/dps-app//div/header/dps-navigation-header//header/div/div[2]/ul/li[2]/dps-login//div/dps-button
   '上記の「//」の左部を参考にしてシャドウルートは、dps-app、dps-navigation-header、dps-loginであることが推定できる
   '待機処理エラーになる場合 On Error~で対応
   '(GetShadowRootの処理完了前にFindElementが実行されるとエラーになるケース)
   '============================================
  With driver
    .StartChrome
    Set caps = .CreateCapabilities()
    caps.SetDetachBrowser True 'ドライバを終了してもブラウザは開いたまま
    .OpenBrowser caps
  
    Dim webElem As WebElement
    Dim cnt As Long
    'シャドウルートの階層が深いため、順番に潜っていく必要がある
    .NavigateTo "https://developer.servicenow.com/"
    Set webElem = .FindElement(By.tagName, "dps-app")
    
    cnt = 0
    On Error GoTo Err_Wait
      Set webElem = webElem.GetShadowRoot.FindElement(By.tagName, "dps-navigation-header")
      Set webElem = webElem.GetShadowRoot.FindElement(By.tagName, "dps-login")
    On Error GoTo 0
    
    'ようやくSingn Inをクリックできた
    webElem.Click
    
    'ドライバ終了(画面はそのまま)
    driver.Shutdown
    Exit Sub

Err_Wait:
    If cnt >= 20 Then Stop '最大1秒待機
    .Wait 50
    cnt = cnt + 1
    Resume
    
End With
End Sub

SeleniumVBAでは「GetShadowRoot」を利用して、1階層ずつシャドウルートの深層に潜っていくことができます。GetShadowRootを記述したあとには、コード例のように、ピリオドでつなげて必ず「FindElement(By.~」で続けることになります。

要素がクリックできなかった場合は、その上層にある要素ならクリックできる可能性もあります。若干の試行錯誤が入りますが御承知ください。

5 補足事項

(1)待機処理

GetShadowRootの処理に時間がかかることがあり、処理完了前にFindElementが実行されるとno such shadow rootと表示され、要素が取得できずにエラーになる場合があります。暗黙的待機を設定してもGetShadowRootの処理の待機には適用されません。

その対応として「On Error GoTo 〜」で最大1秒の待機処理を入れています。

(2)XPathの利用

シャドウルート内で検索する際のお約束があり、By.Xpathが使用できなくなりますので、それ以外で代用することが必要となります。

今回の例では、By.TagNameでシャドウルートを特定及びクリックしたいリンクを特定しています。もし、シャドウルート内でも複雑な検索をしたい場合は、By.CssSelectorが使えるのでBy.Xpathの代用になると思います。

6 参考記事

出典を明記した詳細な記事がありますので、以下を御参照ください。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?