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

And*_*son 9 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>
Run Code Online (Sandbox Code Playgroud)

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

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

这将返回空列表,同时有3个新下载.

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

小智 8

还可以使用pyshadow pip 模块,该模块在我的情况下有效,如下例:

from pyshadow.main import Shadow
from selenium import webdriver

driver = webdriver.Chrome('chromedriver.exe')
shadow = Shadow(driver)
element = shadow.find_element("#Selector_level1")
element1 = shadow.find_element("#Selector_level2")
element2 = shadow.find_element("#Selector_level3")
element3 = shadow.find_element("#Selector_level4")
element4 = shadow.find_element("#Selector_level5")
element5 = shadow.find_element('#control-button') #target selector
element5.click() 
Run Code Online (Sandbox Code Playgroud)


Edu*_*scu 5

有时,阴影根元素是嵌套的,而第二个阴影根在文档根中不可见,但在其父级访问的阴影根中可用。我认为最好使用硒选择器并注入脚本只是为了扎根影子:

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()
Run Code Online (Sandbox Code Playgroud)

为了说明这一点,我刚刚在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()
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

以后编辑:

我最近尝试访问内容设置(请参见下面的代码),它包含多个影象根元素,现在您通常不能同时访问动态内容和三个以上的影象元素,而不能先扩展另一个影象根元素这使得不可能实现自动化。上面的答案曾经使用过一段时间,但是仅需一个元素就可以改变位置,您需要始终与检查元素一起走到树上,看看它是否在影子根中,这是自动化的噩梦。

当您发现此时无法单击按钮时,不仅由于阴影根和动态更改而很难仅找到内容设置。

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()
Run Code Online (Sandbox Code Playgroud)


Sup*_*arp 2

您可以使用该driver.executeScript()方法访问网页中的 HTML 元素和 JavaScript 对象。

在下面的示例中,executeScript将返回元素为 的 Shadow 树中存在的所有元素的Promise节点列表。然后你可以执行你的断言测试:<a>idhost

it( 'check shadow root content', function () 
{
    return driver.executeScript( function ()
    {
        return host.shadowRoot.querySelectorAll( 'a' ).then( function ( n ) 
        {
            return expect( n ).to.have.length( 3 )
        }
    } )
} )     
Run Code Online (Sandbox Code Playgroud)

注意:我不懂 Python,所以我使用了 JavaScript 语法,但它应该以相同的方式工作。