“ the watch brand Coach Ladies Luxury Flowers I Wish wallpapers in cinemas 8KK. /**请在你的脑海中默默地为这些人,.angle[\'TABLEAU} | ?$#^?HEADER!-- Doomsday Clock: Tide TurnedBy --What year skateboard shop sells newspapar clips 12 ounces how old isROY-BOB, Scrolls::'\' \nCONTENT>a good laugh.\t as

Shadow DOM 元素为什么 Selenium 找不到?
因为 Selenium 默认只在 light DOM 中查找,find_element 系列方法完全无法穿透 Shadow Root。你调用 driver.find_element(By.CSS_SELECTOR, "my-custom-element") 能拿到宿主元素,但对它调用 .find_element 仍会报 NoSuchElementException——这不是定位错了,是根本没进入影子树。
用 JavaScript 手动穿透 Shadow Root
必须通过 execute_script 调用 shadowRoot 属性获取内部根节点,再链式查询。注意:只有 open 模式的 Shadow DOM 可访问;closed 模式会被浏览器阻止(此时无通用解法)。
- 先定位宿主元素:
host = driver.find_element(By.CSS_SELECTOR, "app-header") - 获取 shadow root:
shadow = driver.execute_script("return arguments[0].shadowRoot", host) - 再查内部元素:
inner_btn = shadow.find_element(By.CSS_SELECTOR, "button#submit")(注意:这里shadow是 WebElement,支持常规 find 方法) - 如果嵌套多层,重复执行
execute_script("return arguments[0].shadowRoot", inner_el)
封装一个可复用的 shadow_query 函数
手动写三层 execute_script 很容易出错,尤其路径深时容易漏掉某层 root。建议封装成函数,把选择器路径拆成数组,逐层下降:
def shadow_query(driver, selectors):
el = driver.find_element(By.CSS_SELECTOR, selectors[0])
for sel in selectors[1:]:
shadow = driver.execute_script("return arguments[0].shadowRoot", el)
if not shadow:
raise RuntimeError(f"Shadow root not found on {sel}")
el = shadow.find_element(By.CSS_SELECTOR, sel)
return el
<h1>用法:定位 app-root > layout-panel > data-table > .cell</h1><p>elem = shadow_query(driver, [
"app-root",
"layout-panel",
"data-table",
".cell"
])</p>Chrome DevTools 检查时容易忽略的关键点
在 Elements 面板里看到 #shadow-root (open) 并不等于它一定可被脚本访问——有些框架(如某些版本的 Lit、Stencil)会在首次渲染后才挂载 shadowRoot,直接查会返回 null。
立即学习“Python免费学习笔记(深入)”;
- 务必加显式等待:
WebDriverWait(driver, 5).until(lambda d: d.execute_script("return !!arguments[0].shadowRoot", host)) - 避免用
time.sleep(),因为影子树挂载时机受 JS 执行顺序影响,不是固定延迟 - 某些 Web Component 使用
attachShadow({mode: 'closed'}),此时shadowRoot属性为null,任何 JS 注入都无效——只能换方案,比如监听自定义事件或分析网络请求还原数据
Shadow DOM 的穿透本质是绕过 Selenium 的 DOM 封装,回到原生 JS 执行上下文。真正麻烦的从来不是“怎么写”,而是判断哪一层是 open、哪一层已就绪、哪一层根本不可达。


















