首页 / DrissionPage 入门教程 / Shadow DOM 处理

DrissionPage 入门教程

Shadow DOM 处理

本教程共 42 篇 · 第 36 篇 · 更新于 2026-07-29 · 约 6 分钟阅读

DrissionPageDrissionPage 入门教程Shadow DOMshadow-rootWeb Components

36. Shadow DOM 处理

本节目标:理解 Shadow DOM 是什么,学会穿透 shadow-root 边界,操作那些被”藏起来”的元素。

Shadow DOM 是什么

Shadow DOM 是浏览器的一项技术。

它可以让开发者把一段 HTML 和 CSS 封装起来,和外部隔离。组件样式不会泄露出去,外部样式也影响不到里面。

听起来挺好,但对自动化来说是个麻烦。普通的 CSS 选择器和 XPath 没法穿透这层边界。

核心概念

几个名词先搞清楚:

  • Shadow Host:被附加了 Shadow DOM 的普通元素
  • Shadow Root:Shadow DOM 的根节点
  • Shadow Boundary:隔离内外的边界

DrissionPage 提供了专门的方法来处理这些。

获取 Shadow Root

shadow_root 属性就能拿到:

from DrissionPage import ChromiumPage

page = ChromiumPage()
page.get('https://example.com')

# 找到宿主元素
host = page.ele('#shadow-host')

# 获取 Shadow Root
shadow_root = host.shadow_root

if shadow_root:
    print('成功获取 Shadow Root')
else:
    print('获取失败,可能是封闭式 Shadow DOM')

注意:Shadow DOM 分开放式(open)和封闭式(closed)。封闭式从外面拿不到 shadow_root。

在 Shadow DOM 里找元素

拿到 Shadow Root 后,操作就和普通 DOM 一样了:

# 查找单个元素
button = shadow_root.ele('.action-btn')

# 查找多个元素
items = shadow_root.eles('.item')

# 也能用其他选择器
input_field = shadow_root.ele('@name=shadow-input')
title = shadow_root.ele('tag:h2')

操作方式也完全一样:

# 点击
shadow_root.ele('.submit-btn').click()

# 输入
shadow_root.ele('input').input('hello')

# 获取文本
text = shadow_root.ele('.content').text

穿透语法

DrissionPage 支持一种简洁的穿透写法。

>>> 一步到位:

# 不用先拿 shadow_root,直接穿透
element = page.ele('#shadow-host >>> .target-btn')

# 多层嵌套也能穿透
deep = page.ele('#outer-host >>> #inner-host >>> .deep-element')

这和 CSS 的 >>> 选择器类似,表示穿透 Shadow Boundary。

处理嵌套 Shadow DOM

Shadow DOM 里还能套 Shadow DOM。

一层层来:

# 第一层
first_root = page.ele('#outer-host').shadow_root

# 在第一层里找第二层的 host
inner_host = first_root.ele('#inner-host')

# 获取第二层 Shadow Root
second_root = inner_host.shadow_root

# 操作最里面的元素
second_root.ele('.target').click()

处理封闭式 Shadow DOM

封闭式 Shadow DOM 没法直接获取 shadow_root。

可以注入脚本强制改写:

# 在页面加载前注入
page.run_js('''
    const original = Element.prototype.attachShadow;
    Element.prototype.attachShadow = function() {
        return original.call(this, { mode: 'open' });
    };
''')

# 然后再刷新页面
page.refresh()

警告:这种方式只适用于页面加载前注入。如果页面已经加载完,就没效果了。

等待 Shadow DOM 加载

动态创建的 Shadow DOM 需要等待:

# 等待宿主元素出现
page.wait.ele_display('#dynamic-host')

# 获取 Shadow Root
host = page.ele('#dynamic-host')
shadow_root = host.shadow_root

# 等待内部元素出现
page.wait.ele_display('#dynamic-host >>> .dynamic-content')

实用技巧

检查元素是否在 Shadow DOM 里

打开浏览器开发者工具,看 Elements 面板里有没有 #shadow-root 标记。

有就说明元素在 Shadow DOM 里。

获取 Shadow DOM 的 HTML

host = page.ele('#host')
shadow_root = host.shadow_root
print(shadow_root.html)

在 Shadow DOM 里执行 JS

host = page.ele('#host')
shadow_root = host.shadow_root

result = shadow_root.run_js('return document.title;')

小结

Shadow DOM 是网页自动化的一个难点。

DrissionPage 提供了 shadow_root 属性和 >>> 穿透语法,让操作变简单了。

核心思路:找到 host -> 拿 shadow_root -> 在里面找元素。封闭式需要额外处理。