python - 为什么定位 Shadow dom 元素在第 5 个元素处失败?

标签 python windows selenium google-chrome

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

有人告诉我应该使用“shadow dom”元素,所以我继续思考如何做到这一点。

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

no such element: Unable to locate element: {"method":"css selector","selector":"settings-on-startup-page"}

如何解决这个问题?

<小时/>

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

最佳答案

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

尝试这样:

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

关于python - 为什么定位 Shadow dom 元素在第 5 个元素处失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57367383/

相关文章:

python - 使用时间戳或日期时间对象对散点图进行颜色编码?

python - 如何在 python 中为单元测试伪造一个缺失的依赖项?

linux - 我们如何将工件从 Linux 复制到 Windows 服务器?

c# - 表单关闭事件

c++ - 如何将数据从一个 Windows 应用程序复制到另一个 Windows 应用程序?

angularjs - 在 Protractor 中创建和解决 promise

Python - Map/Reduce - 如何在使用 DISCO 计数词示例中读取 JSON 特定字段

python - Django 安装问题 No module named django.core

java - 使用 Selenium 在网页中搜索文本时如何忽略?

selenium - Robot Framework : Wait Until Element Is Visible vs. 元素应该是可见的,使用哪个更好?