如何使用Puppeteer访问封闭Shadow DOM内部的元素?
能否用Puppeteer访问封闭Shadow DOM内部元素?
可以实现,但常规方法(直接读取shadowRoot、>>>选择器)对封闭Shadow DOM无效——因为规范明确closed模式下,外部无法通过元素的shadowRoot属性获取引用。我们可以通过篡改原生attachShadow方法的方式,提前保存封闭shadowRoot的引用。
解决方案原理
在页面加载任何脚本之前,注入代码重写HTMLElement.prototype.attachShadow:当组件调用attachShadow({mode: "closed"})时,除执行原生逻辑外,把生成的shadowRoot挂载到元素的自定义属性上,后续就能通过这个属性访问封闭Shadow DOM内容。
实现代码
修改后的Puppeteer脚本
import puppeteer from "puppeteer"; async function start() { const browser = await puppeteer.launch({ headless: true, }); const page = await browser.newPage(); // 在页面加载前注入脚本,重写attachShadow await page.evaluateOnNewDocument(() => { const originalAttachShadow = HTMLElement.prototype.attachShadow; HTMLElement.prototype.attachShadow = function(options) { const shadowRoot = originalAttachShadow.call(this, options); // 封闭模式下,将shadowRoot存入自定义属性 if (options.mode === "closed") { this.__closedShadowRoot = shadowRoot; } return shadowRoot; }; }); await page.goto("http://localhost:8080/iframe-test-nested.html"); // 读取封闭Shadow DOM的innerHTML const innerHTML = await page.evaluate(() => { const container = document.querySelector("body layout-container"); return container?.__closedShadowRoot?.innerHTML; }); console.log(innerHTML); // 输出包含<div>This is what I want</div>的内容 // 直接定位内部元素并获取文本 const targetText = await page.evaluate(() => { const container = document.querySelector("body layout-container"); const div = container?.__closedShadowRoot?.querySelector("div"); return div?.textContent; }); console.log(targetText); // 输出"This is what I want" await browser.close(); } start();
有效性说明
Puppeteer的evaluateOnNewDocument会在页面上下文初始化后、任何脚本执行前注入代码,所以重写逻辑会先于自定义元素定义执行。当组件创建封闭Shadow DOM时,我们的代码会偷偷保存shadowRoot引用,绕过了规范限制。
注意:这属于hack手段,依赖篡改浏览器原生API,可能随浏览器版本更新失效,但目前在Chrome/Chromium(Puppeteer默认浏览器)中稳定可行。
内容的提问来源于stack exchange,提问作者Shadow78
相关产品推荐
相关产品推荐

