Shadow-root元素close模式数据获取终极解决方案

1,304 阅读2分钟

Shadow-root是什么

Shadow DOM(Shadow-root 的基础技术)是 Web 组件(Web Components)规范的一部分,它允许将一个元素的子树(DOM 子结构)封装起来,使其与主文档(DOM)隔离。Shadow-root 是一个特殊的 DOM 元素,它充当封装的根节点,将组件内部的 DOM 结构与外部 DOM 隔离开来。

Shadow DOM 的主要目标是实现封装,以便开发人员可以构建可重用的自定义元素,而无需担心样式和脚本的冲突。通过使用 shadow-root,可以确保组件内部的样式不会影响主文档,同时也可以防止主文档的样式影响组件。

在创建 shadow-root 时,可以选择两种模式:openclosed。在 open 模式下,可以从主文档访问 shadow-root,这有助于调试和开发。在 closed 模式下,shadow-root 无法从主文档访问,从而提供更严格的封装。

简而言之,shadow-root 是一种实现 DOM 封装的技术,用于创建可重用和独立的自定义 Web 组件。

如何获取Shadow-root元素close模式时的数据

要获取 shadow-root 元素内的数据,您需要使用支持 JavaScript 执行的库,如 Selenium(Python)或 Puppeteer(Node.js)。这里以 Node.js 的 puppeteer 为例,说明如何获取 shadow-root 内的数据。

示例网站链接 image.png

示例代码
const puppeteer = require('puppeteer');
const url = 'https://mil.huanqiu.com/';
const executablePath = './chromedriver.exe';
// const executablePath = './Chrome.app/Contents/MacOS/Google Chrome';
(async () => {
    try {
        const browser = await puppeteer.launch({
            executablePath: executablePath,  // 设置 Chrome 可执行文件路径
            headless: false,
        });
        const script = `
            (function() {
                Element.prototype._attachShadow = Element.prototype.attachShadow;
                Element.prototype.attachShadow = function () {
                    return this._attachShadow({mode:'open'});
                };})();`;
        const page = await browser.newPage();
        await page.evaluateOnNewDocument(script);  // 注入时机:请求前注入替换属性方法
        await page.goto(url);
        await page.waitForTimeout(3000);  // 等待 3 秒

        const getNestedShadowRootData = async (selectors, index = 0) => {
            return await page.evaluate((selectors, index) => {
                const findShadowRoot = (element, selectors, index) => {
                    if (!element || index >= selectors.length) {
                        return element ? element.innerHTML : null;
                    }
                    const shadowHost = element.querySelector(selectors[index]);
                    if (!shadowHost) {
                        return null;
                    }
                    const shadowRoot = shadowHost.shadowRoot;
                    return findShadowRoot(shadowRoot, selectors, index + 1);
                };

                return findShadowRoot(document, selectors, index);
            }, selectors, index);
        };

        // 多层 shadow-root遍历获取数据
        const shadowHostSelectors = [
            'body > channel-container-template',
            'div > div > div > div.col-10 > div.layout > div.layout-center > layout-block-template:nth-child(2)',
            'div > layout-bd-template',
            'div > sketch-feed-template'
        ];
        const nestedShadowRootData = await getNestedShadowRootData(shadowHostSelectors);
        console.log('Nested shadow-root data:', nestedShadowRootData);

        await browser.close();
    } catch (error) {
        console.error('An error occurred:', error);
    }
})();