You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Puppeteer获取客户端渲染页面的完整HTML+CSS?

问题原因
  1. 动态样式加载未完成:像Tesla这类现代网站大量依赖JS动态注入CSS或加载外部样式表,page.content()获取的是当前DOM的静态快照,但即便触发了networkidle0这类网络空闲事件,仍可能有异步执行的JS还在处理样式注入,导致部分CSS没写入DOM。
  2. 无头模式视口不匹配:Puppeteer默认无头模式的视口尺寸很小(通常是800x600),网站的响应式CSS会根据视口加载对应样式,此时DOM中的样式可能是适配小屏的版本,甚至部分大屏样式还没触发加载;而page.screenshot()会基于当前渲染状态截图,视觉上呈现正常,但DOM里的样式还没更新到对应状态。
  3. Shadow DOM样式隔离:部分网站用Shadow DOM封装组件,page.content()只会返回主DOM结构,不会展开Shadow DOM内部的内容和样式,这部分样式在截图时能正常渲染,但不会出现在获取的HTML里。
  4. 延迟加载样式未触发:有些CSS是在页面滚动、用户交互后才加载,page.goto完成后页面没有触发这些交互,对应的样式就不会被加载,导致HTML里缺失这部分内容。
解决办法

1. 模拟正常浏览器视口

设置和普通桌面浏览器一致的视口,确保响应式样式正常加载:

async function getHtml(url) {
    const browser = await puppeteer.launch({
        headless: 'new', // 新版无头模式更接近真实浏览器
        args: ['--no-sandbox']
    });

    const page = await browser.newPage();
    // 设置桌面端标准视口
    await page.setViewport({ width: 1920, height: 1080 });
    await page.goto(url, { waitUntil: 'networkidle0' });
    const k = await page.content()
    await browser.close();
    return k
};

2. 等待样式完全生效

通过等待特定元素的样式或类名出现,确保样式加载完成:

// 在page.goto之后添加:
// 等待页面标记加载完成的类名出现
await page.waitForSelector('body.tesla-page-loaded');
// 或者等待关键元素的样式属性生效
await page.waitForFunction(() => {
    return getComputedStyle(document.querySelector('.hero-banner')).backgroundImage !== 'none';
});

3. 内联所有已渲染样式

如果要让保存的HTML能直接在本地正常显示,需要把外部CSS和计算后的样式内联到元素中,避免外部链接失效:

async function getHtml(url) {
    const browser = await puppeteer.launch({
        headless: 'new',
        args: ['--no-sandbox']
    });

    const page = await browser.newPage();
    await page.setViewport({ width: 1920, height: 1080 });
    await page.goto(url, { waitUntil: 'networkidle0' });

    // 把所有元素的计算样式内联到style属性
    await page.evaluate(() => {
        document.querySelectorAll('*').forEach(el => {
            const computedStyle = getComputedStyle(el);
            el.style.cssText = computedStyle.cssText;
        });
        // 移除外部样式表和内部style标签,避免样式冲突
        document.querySelectorAll('link[rel="stylesheet"], style').forEach(el => el.remove());
    });

    const k = await page.content()
    await browser.close();
    return k
};

4. 处理Shadow DOM

如果网站使用了Shadow DOM,需要遍历并提取内部内容合并到主DOM:

// 在page.goto之后添加:
await page.evaluate(() => {
    function flattenShadowDom(element) {
        const shadowRoot = element.shadowRoot;
        if (shadowRoot) {
            const clone = element.cloneNode(false);
            while (shadowRoot.firstChild) {
                clone.appendChild(flattenShadowDom(shadowRoot.firstChild));
            }
            element.replaceWith(clone);
            return clone;
        }
        return element;
    }
    flattenShadowDom(document.body);
});

5. 延长等待时间(应急方案)

如果不确定具体等待条件,可以适当延长等待时间,确保异步样式加载完成:

await page.goto(url, { waitUntil: 'networkidle0' });
await page.waitForTimeout(3000); // 等待3秒,可根据实际情况调整

内容的提问来源于stack exchange,提问作者Vayun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 18:15:59