python - 如何从 Selenium 处理 Shadow DOM 中的元素

标签 python selenium selenium-webdriver shadow-dom

我想在 chromedriver 中自动检查文件下载完成情况。 下载列表中每个条目的 HTML 看起来像

<a is="action-link" id="file-link" tabindex="0" role="link" href="http://fileSource" class="">DownloadedFile#1</a>

所以我使用下面的代码来查找目标元素:

driver.get('chrome://downloads/')  # This page should be available for everyone who use Chrome browser
driver.find_elements_by_tag_name('a')

当有 3 个新下载时,这将返回空列表。

正如我发现的那样,只能处理 #shadow-root (open) 标签的父元素。 那么如何在这个 #shadow-root 元素中找到元素呢?

最佳答案

有时影子根元素是嵌套的,第二个影子根在文档根中不可见,但在其父访问的影子根中可用。我认为最好使用 selenium 选择器并注入(inject)脚本以获取影子根:

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 个嵌套的影子根元素: enter image description here

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()

稍后编辑:

我最近尝试访问内容设置(见下面的代码),它有多个影子根元素,现在你不能在不先扩展另一个的情况下访问一个,而你通常也有动态内容和超过 3 个影子元素一个变成另一个它使自动化成为不可能。上面的答案在几年前使用过,但只需要一个元素改变位置就足够了,你需要始终检查元素并在树上查看它是否在影子根中,自动化噩梦。

当您发现此时按钮不可点击时,由于 shadowroots 和动态变化,不仅很难找到内容设置。

driver = webdriver.Chrome()


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

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

root2 = shadow_root1.find_element_by_css_selector('[page-name="Settings"]')
shadow_root2 = expand_shadow_element(root2)

root3 = shadow_root2.find_element_by_id('search')
shadow_root3 = expand_shadow_element(root3)

search_button = shadow_root3.find_element_by_id("searchTerm")
search_button.click()

text_area = shadow_root3.find_element_by_id('searchInput')
text_area.send_keys("content settings")

root0 = shadow_root1.find_element_by_id('main')
shadow_root0_s = expand_shadow_element(root0)


root1_p = shadow_root0_s.find_element_by_css_selector('settings-basic-page')
shadow_root1_p = expand_shadow_element(root1_p)


root1_s = shadow_root1_p.find_element_by_css_selector('settings-privacy-page')
shadow_root1_s = expand_shadow_element(root1_s)

content_settings_div = shadow_root1_s.find_element_by_css_selector('#site-settings-subpage-trigger')
content_settings = content_settings_div.find_element_by_css_selector("button")
content_settings.click()

关于python - 如何从 Selenium 处理 Shadow DOM 中的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37384458/

相关文章:

python - 如何防止命令行参数被编码?

python - Django:使用子外键获取父数据

python - gdfs 到图表,反之亦然

python - 使用 Python 抓取 linkedin 连接,但只显示了一些 - Selenium 和 BeautifulSoup

javascript - 如何在下拉 Protractor e2e 测试中选择选项

javascript - 如何在 Protractor 中识别这个元素?

java - 将 python 字典转换为 Java hashmap,其中值类型是数据结构和 lambda 函数的混合

python - 如何获取子类别中所有页面的所有产品(python,amazon)

c# - 如何使用 Jenkins 安排 C# 单元测试?

python - 通过调用页面的 PageObject 导航到该页面