Python の Selenium で ShadowRoot を扱っていたときにちょっとハマったので解決方法を紹介します.
問題
従来,ShadowRootを解析する場合,下記の expand_shadow_element のような関数を用意して,Javascript 経由で element を取得していたと思います.ただ.これは,Chromium バージョン96 以降では動きません.
1 2 3 4 5 6 7 8 9 |
def expand_shadow_element(driver, element): shadow_root = driver.execute_script("return arguments[0].shadowRoot", element) return shadow_root item_root = expand_shadow_element( driver, driver.find_element_by_css_selector("div.XXX") ) item_root.find_element_by_css_selector("div.YYY") |
発生するエラーメッセージは下記のようなものです.
1 2 3 4 |
Traceback (most recent call last): (snip) item_root.find_element_by_css_selector("div.YYY") AttributeError: 'ShadowRoot' object has no attribute 'find_element_by_css_selector' |
解決策
Seleniumが新たに用意した,shadow_root プロパティと find_element を使って,次のように書き換えると動くようになります.
1 2 |
item_root = driver.find_element(By.CSS_SELECTOR, "div.XXX").shadow_root item_root.find_element(By.CSS_SELECTOR, "div.YYY") |
Selenium ライブラリが,W3C WebDriver specification の Get Element Shadow Root に対応したことにより,Javascript を使う必要は無くなりました.また,find_element_by_css_selector は今後廃止予定なのでfind_element に置き換えています.
Selenium ライブラリの変更内容は,『[py] add support for shadow dom end points』が分かりやすいです.
参考文献
- Shadow DOM in Selenium
- https://titusfortner.com/2021/11/22/shadow-dom-selenium.html
Python 以外の言語での対応も含めて紹介されています.
コメント
driver.find_element_by_css_selector(By.CSS_SELECTOR, "div.XXX")
ではなくdriver.find_element(By.CSS_SELECTOR, "div.XXX")
ではないでしょうか?ご指摘ありがとうございます.修正しました.