使用 Selenium 访问 Shadow DOM 树

IT技术 javascript python selenium selenium-chromedriver shadow-dom
2021-03-04 09:50:27

是否可以使用 Selenium/Chrome webdriver 访问 Shadow DOM 中的元素?

正如预期的那样,使用普通元素搜索方法不起作用。在 w3c 上看到了对switchToSubTree规范的引用,但找不到任何实际的文档、示例等。

有人成功了吗?

6个回答

接受的答案不再有效,其他一些答案有一些缺点或不实用(/deep/选择器不起作用且已弃用,document.querySelector('').shadowRoot仅当阴影元素嵌套时才与第一个阴影元素一起使用),有时阴影根元素是嵌套的,第二个影子根在文档根中不可见,但在其父访问的影子根中可用。我认为最好使用 selenium 选择器并注入脚本以获取影子根:

def expand_shadow_element(element):
  shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
  return shadow_root

outer = expand_shadow_element(driver.find_element_by_css_selector("#test_button"))
inner = outer.find_element_by_id("inner_button")
inner.click()

为了理解这一点,我刚刚在 Chrome 的下载页面中添加了一个可测试的示例,单击搜索按钮需要打开 3 个嵌套的阴影根元素: 在此处输入图片说明

import selenium
from selenium import webdriver
driver = webdriver.Chrome()


def expand_shadow_element(element):
  shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
  return shadow_root

driver.get("chrome://downloads")
root1 = driver.find_element_by_tag_name('downloads-manager')
shadow_root1 = expand_shadow_element(root1)

root2 = shadow_root1.find_element_by_css_selector('downloads-toolbar')
shadow_root2 = expand_shadow_element(root2)

root3 = shadow_root2.find_element_by_css_selector('cr-search-field')
shadow_root3 = expand_shadow_element(root3)

search_button = shadow_root3.find_element_by_css_selector("#search-button")
search_button.click()

执行其他答案中建议的相同方法的缺点是它对查询进行了硬编码,可读性较差,并且您不能将中间选择用于其他操作:

search_button = driver.execute_script('return document.querySelector("downloads-manager").shadowRoot.querySelector("downloads-toolbar").shadowRoot.querySelector("cr-search-field").shadowRoot.querySelector("#search-button")')
search_button.click()
@vnportnoy 它应该可以工作,还没有测试过,所以无法确认
2021-04-27 09:50:27
你知道这是否也适用于 firefox geckodriver 吗?
2021-05-07 09:50:27

还应该注意的是,Selenium 二进制 Chrome 驱动程序现在支持 Shadow DOM(自 2015 年 1 月 28 日起):http : //chromedriver.storage.googleapis.com/2.14/notes.txt

正如下面的 Eduard Florinescu 和 Djangofan 所指出的,这已经过时了,浏览器已经赶上了。时代在变。
2021-04-26 09:50:27

我正在使用 C# 和 Selenium,并设法使用 java 脚本在嵌套的 shadow DOM 中找到一个元素。这是我的 html 树:

html 树

我想要最后一行的 url 并获得它,我首先选择“downloads-manager”标签,然后选择它下方的第一个影子根。一旦进入阴影根,我想找到最接近下一个阴影根的元素。该元素是“下载项”。选择后,我可以进入第二个影子根。从那里我通过 id = "file-icon" 选择包含 url 的 img 项目。最后,我可以获得包含我正在寻找的 url 的属性“src”。

两行 C# 代码可以解决这个问题:

IJavaScriptExecutor jse2 = (IJavaScriptExecutor)_driver;
var pdfUrl = jse2.ExecuteScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-item').shadowRoot.getElementById('file-icon').getAttribute('src')");

通常你会这样做:

element = webdriver.find_element_by_css_selector(
    'my-custom-element /deep/ .this-is-inside-my-custom-element')

希望这将继续有效。


但是,请注意/deep/::shadow弃用(并且未在 Opera 和 Chrome 以外的浏览器中实现)。有很多关于在静态配置文件中允许它们的讨论。意思是,查询它们会起作用,但不能设置样式。

如果不想依赖,/deep/或者::shadow因为他们的未来有点不确定,或者因为你想更好地跨浏览器工作,或者因为你讨厌弃用警告,请庆幸还有另一种方式:

# Get the shadowRoot of the element you want to intrude in on,
# and then use that as your root selector.
shadow_root = webdriver.execute_script('''
    return document.querySelector(
        'my-custom-element').shadowRoot;
    ''')
element = shadow_root.find_element_by_css_selector(
    '.this-is-inside-my-custom-element')

更多关于这个: