【问题标题】:Why does targeting shadow dom elements fail at the 5th element?为什么定位 shadow dom 元素在第 5 个元素处失败?
【发布时间】:2019-12-13 11:54:21
【问题描述】:

最近在这里询问如何在 chrome 设置中定位元素:How to edit chromes search and homepage with selenium/python?

有人告诉我应该使用“shadow dom”元素,所以我继续想办法做到这一点。

我能够使用 shadow dom inception 成功定位 chrome 下载页面上的搜索字段,但是当应用几乎相同的逻辑/代码来定位 chrome://settings/ 上的特定页面或一组页面时,python返回错误

没有这样的元素:无法找到元素:{“method”:“css 选择器","选择器":"启动页面上的设置"}

我该如何解决这个问题?


Eduard Florinescu 评论道:“当您通常同时拥有动态内容和超过 3 个阴影元素时,自动化是不可能的。” 这里:How to handle elements inside Shadow DOM from Selenium

我希望有人可以在这里解释限制或解决方法?

这是用于在 Chrome 下载页面上定位搜索字段的示例代码

import selenium
from selenium import webdriver
driver = webdriver.Chrome("C:/Users/John/Desktop/Documents/selenium/webdrivers/chromedriver.exe")

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


#start doing inception
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_tag_name('downloads-toolbar')
shadow_root2 = expand_shadow_element(root2)

root3 = shadow_root2.find_element_by_tag_name('cr-toolbar')
shadow_root3 = expand_shadow_element(root3)

root4 = shadow_root3.find_element_by_css_selector("cr-toolbar-search-field")
shadow_root4 = expand_shadow_element(root4)

root5 = shadow_root4.find_element_by_id("searchInput")

root5.send_keys('test')

以下示例代码不适用于打开特定页面或一组页面单选按钮。

from selenium import webdriver
driver = webdriver.Chrome("C:/Users/John/Desktop/Documents/selenium/webdrivers/chromedriver.exe")


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


#start doing inception
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_tag_name('settings-main')
shadow_root2 = expand_shadow_element(root2)

root3 = shadow_root2.find_element_by_tag_name('settings-basic-page')
shadow_root3 = expand_shadow_element(root3)

root4 = shadow_root3.find_element_by_css_selector("settings-section")
shadow_root4 = expand_shadow_element(root4)

root5 = shadow_root4.find_element_by_css_selector("settings-on-startup-page")
shadow_root5 = expand_shadow_element(root5)

root6 = shadow_root5.find_element_by_css_selector("settings-radio-group")
shadow_root6 = expand_shadow_element(root6)

root7 = shadow_root6.find_element_by_name("4")

root7.click()

如您所见,两个代码片段在语法和结构上几乎相同,但第二个不起作用。

C:\Users\John\Desktop\Documents\selenium\projects\startpage_domain_test\venv\Scripts\python.exe C:/Users/John/Desktop/Documents/selenium/projects/startpage_domain_test/startpage_domain_test.py
Traceback (most recent call last):
  File "C:/Users/John/Desktop/Documents/selenium/projects/startpage_domain_test/startpage_domain_test.py", line 26, in <module>
    root5 = shadow_root4.find_element_by_css_selector("settings-on-startup-page")
  File "C:\Python37-32\lib\site-packages\selenium\webdriver\remote\webelement.py", line 430, in find_element_by_css_selector
    return self.find_element(by=By.CSS_SELECTOR, value=css_selector)
  File "C:\Python37-32\lib\site-packages\selenium\webdriver\remote\webelement.py", line 659, in find_element
    {"using": by, "value": value})['value']
  File "C:\Python37-32\lib\site-packages\selenium\webdriver\remote\webelement.py", line 633, in _execute
    return self._parent.execute(command, params)
  File "C:\Python37-32\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "C:\Python37-32\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"settings-on-startup-page"}
  (Session info: chrome=75.0.3770.142)


Process finished with exit code 1

【问题讨论】:

    标签: python windows selenium google-chrome


    【解决方案1】:

    看起来您只需要使用shadowRoots 递归遍历元素。

    试试这样:

    def find_in_shadow_dom(css):
      return driver.execute_async_script("""
        const traverse = e => {
          let el
          if(el = e.querySelector('""" + css + """')){
            arguments[0](el)
          }
          [...e.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => traverse(e.shadowRoot))
        }
        [...document.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => traverse(e.shadowRoot))
        arguments[0](null)
      """)
    
    input = find_in_shadow_dom('#searchInput')
    input.send_keys('testing')
    

    编辑:设置页面的注释..

    control = find_in_shadow_dom('[label="Open a specific page or set of pages"]')
    # don't forget to scroll into view
    driver.execute_script("arguments[0].scrollIntoView(true)", control)
    control.click()
    

    【讨论】:

    • 我尝试使用此代码 sn-p,它有时可以工作,但尝试定位 chrome://settings/ 上的“打开特定页面或一组页面”单选按钮会返回错误 AttributeError: 'NoneType' 对象没有属性 'click' 当我尝试点击它时: input = find_in_shadow_dom('span[label="打开一个特定页面或一组页面"]') input.click()
    • 尝试使用此函数定位其父容器时出现类似问题:input = find_in_shadow_dom('controlled-radio-button[label="Open a specific page or set of pages"]') -> selenium.common.exceptions.ElementClickInterceptedException:消息:元素点击被拦截:元素在点(631、1418)处不可点击
    【解决方案2】:

    AFAIK,访问阴影元素没有这样的限制。在提供正确的阴影树元素之前,您应该能够很好地访问它们。

    详细解释请参考答案here。内容丰富且详细..

    这里是可以传递和返回元素的js

    input = driver.execute_script("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar-search-field#search').shadowRoot.querySelector('input#searchInput')")
    input.send_keys('testing')
    

    截图:

    【讨论】:

    • 我能够复制粘贴此代码并使其与 chrome://downloads 上的搜索栏一起使用,但是当我尝试使用相同的基本结构单击 chrome 上的“添加”按钮时://settings/searchEngines 我得到一个错误。为什么它在第一个示例中有效,而在第二个示例中无效?
    • url = "chrome://settings/searchEngines" driver.get(url) input = driver.execute_script("return document.querySelector('settings-ui').shadowRoot.querySelector('settings) -main#main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section').shadowRoot.querySelector('settings-search-page').shadowRoot.querySelector('settings -animated-pages#pages').shadowRoot.querySelector('settings-subpage').shadowRoot.querySelector('settings-search-engines-page').shadowRoot.querySelector('paper-button#addSearchEngine')") 输入.click()
    • selenium.common.exceptions.JavascriptException:消息:javascript 错误:无法读取 null 属性“shadowRoot”(会话信息:chrome=76.0.3809.100)
    • 不确定您定位的是哪个元素。如屏幕截图所示,我能够成功指向元素。
    猜你喜欢
    • 1970-01-01
    • 2021-07-27
    • 2021-08-28
    • 2019-02-27
    • 2015-04-25
    • 2020-03-16
    • 2013-07-06
    • 2014-11-24
    • 2021-01-04
    相关资源
    最近更新 更多