Skill 方案 S6 · 整站长图截图
一句话:给一个域名,自动收集站内页面、逐页整页截图、纵向拼成一张 PNG,并给出可核对的尺寸与失败页;3 次截图任务消耗的 0.09 亿 token 可压到 0.01 亿。
一、为什么需要它
截图看似最简单,实际是单次成本最贵的操作之一:单次会话 4.57M token,因为要临时写 Playwright 脚本、调滚动、处理懒加载、拼图,每一步都在试。
| 指标 | 数值 | 说明 |
|---|---|---|
| 截图任务 | 3 次 | 门户整站、单页存档、对比图 |
| 累计 token | 0.09 亿 | 单次最高 457 万 |
| 平均工具调用 | 30–60 次 | 大部分是失败重试 |
| 一次成功 | 0 次 | 三次都改了参数重跑 |
| 现有资产 | site-longshot skill | 已封装 capture.mjs,但触发不稳定 |
四个反复出现的具体问题:
- 临时手写脚本。每次都要重新写
chromium.launch→goto→screenshot({fullPage:true}),然后发现懒加载没触发、fixed 导航重复出现。 - 等待时间靠猜。
waitForTimeout(1000)有时不够,有时白等,页面高度在不同次截图里能差 30%。 - 拼接错位。分段截图时若视口高度变化,
100vh元素会在每段重复,拼出来一条一条的。 - 超长图被浏览器拒绝。单页高度超过 40000 px 时
fullPage直接失败,或生成一张 300 MB 的图打不开。
二、触发词与边界
| 触发词 | 不适用场景 |
|---|---|
| 「网站截图」「整页长图」「长截图」 | 只要某个元素/某个视口 → 浏览器截图即可 |
| 「全站截图」「把网页存成一张图」 | 要 PDF 而不是 PNG → 用打印/导出 |
| 「网页存档」「截全站生成一张长图」 | 要抓文字内容 → 走抓取/解析,不截图 |
| 「页面太长截不全」 | 需要登录且拿不到登录态 → 明确说明无法截取 |
边界:只截公开可访问页面,默认同域、不提交表单、不下载文件;需要登录态时必须显式传 --storage-state,不得用公开缓存冒充登录后内容。
三、输入与前置检查
先跑环境自检,ok: true 才继续:
node "C:/Users/cag/.workbuddy/skills/site-longshot/scripts/capture.mjs" --check
本机返回 {"ok":true,"node":"v22.22.2","browsersInstalled":["chromium-1243"],"systemEdge":true},说明 Playwright 与 Chromium 都在,不需要 sharp、Pillow、ImageMagick——拼图是浏览器 canvas 完成的。
URL 列表来源,按优先级三选一:
| 来源 | 做法 | 适用 |
|---|---|---|
| 用户给定 | 直接列 URL | 最准,优先 |
sitemap.xml | Invoke-WebRequest https://cag.quest/sitemap.xml 取 <loc> | 站点有 sitemap 时 |
| 站内 BFS | --max-pages N --depth D 自动爬同域链接 | 无 sitemap、只要主要页面 |
站点规模与页数对应:门户类 3–5 页,文档站 8–15 页,超过 15 页按栏目拆多次跑。
四、执行步骤
步骤 1 · 定视口与设备像素比。 默认 1440 × 1,与桌面浏览一致;要存档级清晰度用 --scale 2,体积约 4 倍,超长页不建议开。
步骤 2 · 等待策略。 三层等待缺一不可:
| 层 | 参数 | 建议值 |
|---|---|---|
| 首屏加载 | --wait | 800 ms(默认);动态站 2500 ms |
| 懒加载预热 | --scroll-step / --scroll-delay | 700 px / 120 ms(默认) |
| 动画与 fixed 冻结 | 默认开启 | 不要关 |
字体加载是隐藏坑:webfont 未就绪时截出来是回退字体,中文字宽变化会让排版整体偏移。当前 capture.mjs 没有独立字体等待参数,用 --wait 2500 兜底;若要精确,需在 goto 之后加 await page.evaluate(() => document.fonts.ready)(需按实际调整,改脚本后同步 SKILL.md)。
步骤 3 · 单页整页长图。
node "C:/Users/cag/.workbuddy/skills/site-longshot/scripts/capture.mjs" "https://cag.quest" \
--width 1440 --wait 2000 \
--out "C:/Users/cag/WorkBuddy/2026-11-11-09-21-47/tmp-longshot/cag-quest-home.png"
步骤 4 · 整站多页拼接。
node "C:/Users/cag/.workbuddy/skills/site-longshot/scripts/capture.mjs" "https://cag.quest" \
--max-pages 8 --depth 2 --width 1440 --wait 2000 \
--save-pages \
--out "C:/Users/cag/WorkBuddy/2026-11-11-09-21-47/tmp-longshot/cag-quest-all.png"
--save-pages 一定要开:拼接出错时能立刻定位是哪一页,而不是重跑整站。
步骤 5 · 超长图分片。 单页超过 40000 px 会被裁剪并告警,最终 PNG 超过 30000 px 会被等比缩小(downscaleRatio < 1)。要原尺寸就分片:
node ".../capture.mjs" "https://cag.quest/notes/" --chunk 1200 --max-height 60000 --no-downscale --out ".../notes-part.png"
--no-downscale 让超限变成显式报错而不是悄悄缩小,配合 --chunk 1200 固定每段视口高度,避免 100vh 抖动造成重复条带。
步骤 6 · 校验并交付。 读 stdout 的 JSON,确认 ok、height > width、pages[].status 全 200、failed 为空;用 present_files 展示 PNG 并给出路径、尺寸、体积、覆盖页数。warnings 里的裁剪/缩小必须原样转述。
五、输出与验收
{"ok":true,"output":"...cag-quest-all.png","width":1440,"height":18420,"bytes":8321440,
"pages":[{"url":"https://cag.quest/","status":200,"height":3120,"chunks":3}],
"failed":[],"warnings":[],"downscaleRatio":1}
三条硬断言:
ok === true且输出文件存在,bytes > 100 KB;一张全白的图也可能"成功",所以必须再看尺寸。- 每页
status为 200,failed为空;有失败页就如实列出,不许用缓存图替代。 downscaleRatio === 1或已在回复中说明缩小比例;悄悄缩小的长图会丢字,属于隐性失败。
六、失败处理与回滚
| 现象 | 根因 | 动作 |
|---|---|---|
| 图里导航重复出现 | fixed/sticky 未冻结 | 确认没加 --no-freeze |
| 底部大片空白 | 懒加载未触发 | 加 --wait 2500、--scroll-delay 250 |
| 中文变宋体/错位 | webfont 未加载完 | 加大 --wait,或按上文加 document.fonts.ready |
executable doesn't exist | 浏览器未装 | --check 看 browsersInstalled,用 --browser edge 兜底 |
| 拼接错位 | 分段视口高度不一致 | 固定 --chunk,不要混用 --scale |
| 图太大打不开 | 超 --max-output-height | 分片输出,或按栏目拆多次 |
回滚:截图是无副作用的只读操作,失败重跑即可;产物统一落 tmp-longshot/,过期文件移到 _trash,不直接删除。
七、收益测算
| 项目 | 现状 | 改造后 |
|---|---|---|
| 单次 token | 300 万 | 25 万 |
| 次数 | 3 次 | 3 次 |
| 累计 | 0.09 亿 | 0.01 亿 |
| 一次成功率 | 0 | 80% 以上 |
| 单次耗时 | 5–15 分钟 | 1–3 分钟 |
省的是"写脚本 + 调试等待 + 重跑"三轮。已有 site-longshot 把这三轮压成一条命令,本方案要做的是把触发词补全(当前对"全站""存档""截不全"覆盖不足),并在正文里把分片与字体两个坑前置,避免每次重新发现。节省 0.08 亿,约 89%,是七类里比例最高的一项。