如何用Headless Chrome录制网站视频?求服务器端捕获CSS动画为MP4的方案
用Headless Chrome录制网站视频(含CSS动画转MP4)的实用方案
嘿,这个需求我太熟了!之前帮团队做过好几个CSS动画的服务器端录制任务,下面给你梳理几个靠谱的方案,从原生Headless Chrome+Puppeteer的手动实现,到更省心的工具都有:
一、用Puppeteer + FFmpeg手动合成视频
Puppeteer本身没有直接录视频的API,但我们可以通过捕获页面帧再合成视频的方式实现,特别适合精准控制CSS动画的录制时机:
核心步骤:
- 启动Headless Chrome并加载目标页面:确保CSS动画能自动播放(有些页面可能需要触发,或者设置
prefers-reduced-motion让动画正常运行) - 定时捕获页面截图:按照动画的帧率(比如30fps),每隔
1000/30毫秒截取一帧 - 用FFmpeg将帧序列合成MP4:把所有截图按顺序拼接成流畅的视频
示例代码片段:
const puppeteer = require('puppeteer'); const fs = require('fs'); const { exec } = require('child_process'); (async () => { const browser = await puppeteer.launch({ headless: 'new' }); const page = await browser.newPage(); await page.setViewport({ width: 1280, height: 720 }); // 确保CSS动画不被"减少动画"偏好禁用 await page.emulateMediaFeatures([{ name: 'prefers-reduced-motion', value: 'no-preference' }]); await page.goto('你的目标页面URL'); // 等待动画完全加载(比如等某个元素的动画类出现) await page.waitForSelector('.animated-element'); // 创建临时目录存帧 const frameDir = './temp-frames'; if (!fs.existsSync(frameDir)) fs.mkdirSync(frameDir); // 捕获30秒的帧(30fps,共900帧) const fps = 30; const duration = 30; for (let i = 0; i < fps * duration; i++) { await page.screenshot({ path: `${frameDir}/frame-${i.toString().padStart(4, '0')}.png` }); await new Promise(resolve => setTimeout(resolve, 1000/fps)); } await browser.close(); // 用FFmpeg合成MP4 exec(`ffmpeg -r ${fps} -i ${frameDir}/frame-%04d.png -c:v libx264 -pix_fmt yuv420p output.mp4`, (err, stdout, stderr) => { if (err) { console.error(`合成失败: ${err}`); return; } console.log('视频合成完成!'); // 可选:删除临时帧文件 fs.rmSync(frameDir, { recursive: true }); }); })();
注意点:
- 帧率要和CSS动画的
animation-duration、animation-iteration-count匹配,避免卡顿或快进 - 如果动画是无限循环的,可以设置固定录制时长,或者监听动画结束事件再停止截图
二、更省心的服务器端工具推荐
如果不想手动拼帧,这些工具能帮你省不少事:
1. Playwright(强烈推荐)
作为Puppeteer的继任者,Playwright原生支持视频录制,不用依赖外部工具,代码极简,还支持Chrome、Firefox、Safari多浏览器:
const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ recordVideo: { dir: './recordings/' } // 自动录制视频到指定目录 }); const page = await context.newPage(); await page.setViewport({ width: 1280, height: 720 }); await page.emulateMediaFeatures([{ name: 'prefers-reduced-motion', value: 'no-preference' }]); await page.goto('你的目标页面URL'); await page.waitForTimeout(30000); // 等待30秒动画完成 // 或者监听动画结束事件:await page.waitForFunction('document.querySelector(".animated-element").getAnimations()[0].finished') await context.close(); await browser.close(); console.log('视频录制完成,已保存到./recordings/'); })();
Playwright会自动处理帧捕获和编码,生成的MP4质量很高,而且支持设置视频尺寸、帧率等参数,非常适合服务器端批量录制。
2. FFmpeg + Chrome DevTools Protocol(CDP)
如果不想用Node.js库,可以直接通过CDP控制Headless Chrome,同时用FFmpeg捕获Chrome的视频流。这种方式适合纯命令行环境:
# 启动Headless Chrome并开启CDP端口 google-chrome --headless=new --remote-debugging-port=9222 --window-size=1280,720 https://your-target-page.com & # 用FFmpeg通过CDP捕获视频流 ffmpeg -f chrome -i http://localhost:9222 output.mp4
这个方案不需要写代码,直接用命令行就能完成,适合快速测试或脚本自动化。
3. Puppeteer-Recorder(第三方封装库)
这是一个专门为Puppeteer做的录制工具,封装了截图和FFmpeg合成的逻辑,API更简洁:
const PuppeteerRecorder = require('puppeteer-recorder'); (async () => { await PuppeteerRecorder.record({ browser: 'chrome', url: '你的目标页面URL', viewport: { width: 1280, height: 720 }, output: 'css-animation.mp4', fps: 30, duration: 30, emulateMediaFeatures: [{ name: 'prefers-reduced-motion', value: 'no-preference' }] }); })();
三、针对CSS动画的优化技巧
- 禁用"减少动画"偏好:Headless Chrome默认可能会尊重
prefers-reduced-motion,所以一定要用emulateMediaFeatures强制开启动画 - 固定视口:录制前设置固定的视口大小,避免页面响应式变化影响录制效果
- 关闭干扰元素:用
page.$eval隐藏弹窗、广告等不需要的元素,确保动画是画面焦点 - 预加载资源:用
page.waitForLoadState('networkidle')等待页面资源完全加载后再开始录制,避免动画卡顿
内容的提问来源于stack exchange,提问作者Mikhail Novikov




