全页截图

full-page-screenshot
分类通用
作者Alireza Rezvani
许可MIT
评分4.80/5
使用12.3K

全页截图 (Full Page Screenshot)

通过 Chrome DevTools Protocol 对任何网页进行全页截图。生成一张包含所有内容的单张 PNG 图片(即使是需要滚动才能看到的部分)。除 Node.js 22+ 和开启了远程调试的 Chrome 外,无需任何外部依赖。

前置条件

  • Node.js 22+ (使用内置的 WebSocket)
  • Chrome/Chromium 且已启用远程调试

检查环境就绪情况:

bash
node "${SKILL_DIR}/scripts/full-page-screenshot.mjs" --check

如果 Chrome 检查失败,请指导用户打开 chrome://inspect/#remote-debugging 并启用 "Allow remote debugging for this browser instance"

工作流

方案 A:对已打开的标签页截图(推荐用于需要登录的页面)

1. 列出可用标签页:

bash
node "${SKILL_DIR}/scripts/full-page-screenshot.mjs" --list

2. 通过标题/URL 确定目标 ID,然后执行捕获:

bash
node "${SKILL_DIR}/scripts/full-page-screenshot.mjs" <targetId> /tmp/screenshot.png --width 1200 --dpr 1

方案 B:对指定 URL 截图(打开后台标签页 $\rightarrow$ 捕获 $\rightarrow$ 关闭)

bash
node "${SKILL_DIR}/scripts/full-page-screenshot.mjs" --url "https://example.com" /tmp/screenshot.png --width 1200 --dpr 1 --wait 15000

> 注意: --url 模式会创建后台标签页。对于需要身份验证的页面(如 SSO、登录墙),请使用方案 A。

参数说明

| 参数 | 描述 | 默认值 |
|-----------|-------------|---------|
| output | 输出 PNG 文件路径 | /tmp/screenshot.png |
| --width | 视口宽度(CSS 像素);文章建议 1200,仪表盘建议 1440-1920 | 1200 |
| --dpr | 设备像素比(2 = Retina 屏,但文件体积会增加 4 倍) | 1 |
| --wait | 页面加载超时时间(毫秒,仅限 --url 模式) | 15000 |
| --css | 捕获前注入的自定义 CSS(例如用于隐藏某些元素) | — |

验证输出

bash
# macOS
sips -g pixelWidth -g pixelHeight /tmp/screenshot.png

Linux

file /tmp/screenshot.png

核心能力

1. SPA 滚动容器扩展 — 检测 overflow-y: auto/scroll 容器,通过滚动触发懒加载,随后移除溢出限制(包括 Tailwind 的 h-[calc(...)]),确保所有内容在单次渲染中完整呈现。

2. DOM 稳定性检测 — 在 readyState=complete 之后,持续监测 DOM 元素数量直到稳定。这确保了 SPA 框架已完成动态内容的渲染。

3. 触发懒加载 — 逐步滚动视口以触发 IntersectionObserver 回调,并等待所有 <img> 元素加载完成。

4. 超长页面分块捕获 — 超过 16,000px 的页面将以 8,000px 为块进行捕获,并使用 Python PIL 自动拼接。若未安装 PIL,则分别保存分块图片。

5. Chrome 自动发现 — 读取 DevToolsActivePort 文件以查找调试端口。若失败,则尝试探测 9222, 9229, 9333 端口。

6. CDP 代理回退 — 当 CDP 代理持有浏览器 WebSocket 时,脚本将回退到使用代理 API 端点 (/eval, /screenshot, /scroll) 进行捕获。

工作原理

code
1. 发现 Chrome 调试端口
2. 通过 WebSocket (CDP) 连接
3. 附加到目标页 / 创建后台标签页
4. 通过 Emulation 域设置视口宽度 5. 等待:readyState + DOM 稳定性 6. 检测并展开滚动容器 7. 滚动页面(触发懒加载) 8. 等待图片加载完成 9. 测量最终内容高度 10. Page.captureScreenshot(或分块截屏) 11. 如有需要,拼接分块(使用 PIL) 12. 还原视口,断开连接,清理资源

反面模式 (Anti-Patterns)

| 不要这样做 | 建议这样做 |
|--------|-----------|
| 对高度超过 10,000px 的页面使用 --dpr 2 | 使用 --dpr 1 以避免 Chrome 内存问题 |
| 对需要认证/SSO 的页面使用 --url | 在用户已登录的标签页上使用 --list + targetId |
| 为 SPA 设置低于 5000 的 --wait | SPA 需要时间获取数据并渲染;建议使用 10000-15000 |
| 在未先执行 --check 的情况下直接截屏 | 始终验证 Chrome 调试功能是否可用 |
| 为所有页面硬编码统一的视口宽度 | 文章类使用 1200,仪表盘/表格类使用 1440+ |
| 跳过输出验证 | 截屏后始终使用 sipsfile 命令进行验证 |

故障排除

| 现象 | 原因 | 解决方法 |
|---------|-------|-----|
| "Cannot find Chrome debugging port" | 未启用远程调试 | 打开 chrome://inspect/#remote-debugging 并启用 |
| "WebSocket connection timeout" | CDP 代理占用连接 | 脚本会自动回退到代理 API |
| 截屏为空白/白色 | 页面尚未加载完成 | 增加 --wait 的值 |
| 底部被截断 | 滚动容器未展开 | 脚本会自动处理;如果问题持续请提交 Issue |
| 内存溢出 (Out of memory) | 页面极高 + 高 DPR | 将 --dpr 降低至 1 和/或减小 --width |
| "PIL not available for stitching" | 未安装 Python Pillow | 使用 pip3 install Pillow 安装,或接受独立的分块文件 |

交叉引用