使用Puppeteer录制Google Meet音频失败问题求助
Puppeteer录制Google Meet音频为空的解决方案
问题概述
已实现通过Puppeteer以机器人身份加入Google Meet并正常录制视频,但无法捕获会议音频,尝试puppeteer-screen-recorder、puppeteer-stream等工具后,生成的音频文件均为空。
原测试代码
import puppeteer from 'puppeteer-extra'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import { executablePath } from 'puppeteer'; import fs from 'fs'; import AudioRecorder from 'node-audiorecorder'; import * as PuppeteerScreenRecorder from 'puppeteer-screen-recorder' import RecordRTC from 'recordrtc'; import { getStream } from "puppeteer-stream"; const file = fs.createWriteStream("./test.webm"); puppeteer.use(StealthPlugin()); (async () => { const browser = await puppeteer.launch({ headless: false, defaultViewport: null, devtools: false, args: [ "--window-size=1920,1080", "--window-position=1921,0", "--autoplay-policy=no-user-gesture-required", ], ignoreDefaultArgs: ["--mute-audio"], executablePath: executablePath(), }); const page = await browser.newPage(); const navigationPromise = page.waitForNavigation(); const context = browser.defaultBrowserContext(); await context.overridePermissions( "https://meet.google.com/", ["microphone", "camera", "notifications"] ); await page.waitForTimeout(2500); await page.goto('https://meet.google.com/cmp-zzwo-adb' + '?hl=en', { waitUntil: 'networkidle0', timeout: 10000, }); await navigationPromise; await page.waitForSelector('input[aria-label="Your name"]', { visible: true, timeout: 50000, hidden: false, }); await page.waitForTimeout(1000); await page.keyboard.down('ControlLeft'); await page.keyboard.press('KeyE'); await page.keyboard.up('ControlLeft'); await page.waitForTimeout(1000); await page.waitForTimeout(1000); await page.keyboard.down('ControlLeft'); await page.keyboard.press('KeyD'); await page.keyboard.up('ControlLeft'); await page.waitForTimeout(1000); await page.click(`input[aria-label="Your name"]`); await page.type(`input[aria-label="Your name"]`, 'Bot'); await page.click( `button[class="VfPpkd-LgbsSe VfPpkd-LgbsSe-OWXEXe-k8QpJ VfPpkd-LgbsSe-OWXEXe-dgl2Hf nCP5yc AjY5Oe DuMIQc LQeN7 jEvJdc QJgqC"]` ); const stream = await getStream(page, { audio: true, mimeType: "audio/mp3" }); console.log("recording"); stream.pipe(file); const recorder = new PuppeteerScreenRecorder.PuppeteerScreenRecorder(page); await recorder.start('./report/video/simple.webm'); setTimeout(async () => { await recorder.stop(); await stream.destroy(); file.close(); console.log("finished"); await browser.close(); }, 15000) })();
原package.json
{ "name": "own", "version": "1.0.0", "description": "", "main": "index.js", "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "node-audiorecorder": "^3.0.0", "puppeteer": "^19.6.3", "puppeteer-extra": "^3.3.4", "puppeteer-extra-plugin-stealth": "^2.11.1", "puppeteer-screen-recorder": "^2.1.2", "puppeteer-stream": "^2.1.4", "recordrtc": "^5.6.2", "screencap": "^1.0.0", "ws": "^8.12.1" } }
问题原因分析
- 多录制工具冲突:同时使用
puppeteer-stream和PuppeteerScreenRecorder抢占音频资源,导致捕获失败 - 权限与参数缺失:未添加WebRTC音频捕获所需的浏览器启动参数,无法正确识别Meet的媒体流
- 选择器不稳定:使用Google Meet动态生成的class选择器,易因UI更新导致操作失效
- 音频捕获逻辑错误:普通页面音频捕获无法直接获取WebRTC传输的Meet会议音频
解决方案
1. 精简录制逻辑,避免资源冲突
移除重复的puppeteer-stream录制代码,仅保留PuppeteerScreenRecorder并配置音频参数
2. 添加必要的浏览器启动参数
补充WebRTC媒体流捕获所需的参数,绕过设备权限弹窗
3. 替换不稳定选择器
使用aria-label替代动态class选择器,保证脚本稳定性
修改后的完整代码
import puppeteer from 'puppeteer-extra'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import { executablePath } from 'puppeteer'; import * as PuppeteerScreenRecorder from 'puppeteer-screen-recorder' puppeteer.use(StealthPlugin()); (async () => { const browser = await puppeteer.launch({ headless: false, defaultViewport: null, devtools: false, args: [ "--window-size=1920,1080", "--window-position=1921,0", "--autoplay-policy=no-user-gesture-required", "--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--enable-usermedia-screen-capturing", "--allow-file-access-from-files" ], ignoreDefaultArgs: ["--mute-audio"], executablePath: executablePath(), }); const page = await browser.newPage(); const context = browser.defaultBrowserContext(); await context.overridePermissions( "https://meet.google.com/", ["microphone", "camera", "notifications"] ); await page.goto('https://meet.google.com/cmp-zzwo-adb?hl=en', { waitUntil: 'networkidle0', timeout: 10000, }); await page.waitForSelector('input[aria-label="Your name"]', { visible: true, timeout: 50000, }); // 关闭摄像头 await page.keyboard.down('ControlLeft'); await page.keyboard.press('KeyE'); await page.keyboard.up('ControlLeft'); await page.waitForTimeout(1000); // 关闭麦克风 await page.keyboard.down('ControlLeft'); await page.keyboard.press('KeyD'); await page.keyboard.up('ControlLeft'); await page.waitForTimeout(1000); // 输入名称并加入会议 await page.click(`input[aria-label="Your name"]`); await page.type(`input[aria-label="Your name"]`, 'Bot'); await page.click(`button[aria-label="Ask to join"]`); // 等待会议加载完成 await page.waitForTimeout(5000); // 配置带音频的屏幕录制 const recorder = new PuppeteerScreenRecorder.PuppeteerScreenRecorder(page, { audio: { codec: 'opus', bitrate: 128000 }, video: { codec: 'vp8', bitrate: 2500000 }, fps: 30 }); await recorder.start('./report/video/meet-recording.webm'); // 录制15秒后停止 setTimeout(async () => { await recorder.stop(); console.log("录制完成"); await browser.close(); }, 15000); })();
关键修改说明
- 添加
--use-fake-ui-for-media-stream等参数,绕过真实设备权限验证,直接捕获WebRTC流 - 配置
PuppeteerScreenRecorder的音频编码参数,明确指定opus编码确保音频正常捕获 - 用
aria-label="Ask to join"替代动态class选择器,避免UI更新导致按钮点击失效 - 移除冲突的
puppeteer-stream逻辑,确保单一录制工具独占音频资源
内容的提问来源于stack exchange,提问作者Manish Rana
相关产品推荐
相关产品推荐

