截图脚本总是「跑完没产物」?三个无报错失败的排查顺序

3 阅读3分钟

背景

我给自己的日更视频做了一条自动出图链路:一个 HTML 拆成几屏,用无头 Chrome 逐屏截图,再用 ffmpeg 串成 10 秒竖版片。最坑的不是 ffmpeg 的滤镜参数,而是「命令跑完了,终端没报错,产物没出现」这种无声失败。 下面按我实际排查的顺序写,三步能定位八成的「跑完没产物」。

第一步:先确认产物到底有没有生成

不要先改 HTML,也不要怀疑代码。先看文件:

$p = "$env:TEMP\shot\s1.png"
if (!(Test-Path $p)) { 'missing' } else { (Get-Item $p).Length }

长度为 0 和文件不存在是两类问题:前者是渲染成功但写入失败(多半是路径或权限),后者是渲染阶段根本没执行。

第二步:换 headless 模式

我本机遇到的第一个无声失败就是 --headless=new:进程退出码 0,PNG 不生成。换成 --headless=old 一次成功。

Start-Process chrome -ArgumentList @(
  "--headless=old",
  "--disable-gpu",
  "--hide-scrollbars",
  "--user-data-dir=$env:TEMP\p1",
  "--window-size=1080,1920",
  "--screenshot=$env:TEMP\shot\s1.png",
  "file:///C:/Temp/shot/index.html?s=1"
) -Wait

这一条不需要理解原理,先试。换完有产物,就说明问题在 headless 实现,不在你的页面。

第三步:查 Chrome 实例有没有被吞

第二种无声失败是「参数被转交给了已经开着的 Chrome」。Chrome 对同一个 user-data-dir 只允许一个实例,第二个进程会把参数丢给已有实例然后自己退出。 判据:截图结果是你当前浏览器窗口的内容,或者什么都不变。 对策:每一屏一个独立目录,并且每次重跑换新目录:

$profile = Join-Path $env:TEMP ('p_' + [guid]::NewGuid().ToString('N'))

同一个目录连续用两次,还可能命中缓存——改了文案却出旧图,很容易误判成「改了没生效」。

两个容易连带踩的坑

路径里有中文是第三个高频原因。--screenshot 和 file:/// 都走纯 ASCII 路径最稳,工作目录放 %TEMP% 下的英文目录,产物再从那里拷走。 页面的实际尺寸也要写死。body { margin:0; width:1080px; height:1920px },否则截图尺寸和渲染尺寸不一致,文字会偏。

串联时的参数递推

四张图用 xfade 串,offset 要按前一段总时长减过渡时长递推。四屏各 3 秒、过渡 0.3 秒,offset 分别是 2.7 / 5.4 / 8.1,成片 10.5 秒。写错会报找不到输入链路,或者中间出现黑帧。

ffmpeg -y -loop 1 -t 3 -i s1.png -loop 1 -t 3 -i s2.png `
 -filter_complex "[0][1]xfade=transition=fade:duration=0.3:offset=2.7,format=yuv420p[v]" `
 -map "[v]" -r 30 -c:v libx264 -crf 23 out.mp4

一套能节省时间的检查顺序

  1. 产物文件存不存在、大小是不是 0
  2. headless 模式换 old
  3. user-data-dir 是否唯一、路径是否纯 ASCII
  4. 页面尺寸是否写死
  5. xfade 的 offset 是否递推正确 按照这个顺序,我基本不会再把时间花在「怀疑 HTML 写错了」上。真正耗时间的不是修复,而是定位——而定位的前提是先把「无声失败」变成「有信号失败」。