Puppeteer中MutationObserver内无法触发page.eval的问题排查
在Pokernow中通过MutationObserver监听下注变化时,正确调用Puppeteer逻辑的方法
核心问题原因
你遇到的问题本质是混淆了Puppeteer的两个隔离执行上下文:
- Node.js上下文:脚本主逻辑、
page对象、自定义打印函数都运行在这里 - 浏览器上下文:
page.evaluate注入的代码、MutationObserver的回调都运行在浏览器页面环境中
两个上下文无法直接跨域调用函数或方法——之前的写法试图在浏览器上下文里直接调用Node.js的page.evaluate,或是直接调用Node.js环境的打印函数,所以无法触发预期逻辑。
解决方案:两种可行实现方式
方式1:用window.postMessage传递数据到Node.js上下文
通过浏览器的消息机制把下注数据传递到Node.js环境,再执行打印逻辑,是跨上下文通信的常用方案。
- 在Node.js上下文监听页面消息:
// 监听浏览器发送的下注更新消息 page.on('message', (message) => { if (message.type === 'newBet') { console.log(`玩家 ${message.playerNum} 新下注金额:${message.amount}`); } });
- 在浏览器上下文配置MutationObserver,检测变化后发送消息:
await page.evaluate(() => { // 替换为Pokernow实际的玩家DOM选择器 const playerElements = document.querySelectorAll('.lobby-player'); playerElements.forEach((playerEl, idx) => { const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { // 根据页面实际DOM变化类型调整判断条件 if (mutation.type === 'childList' || mutation.target.classList.contains('bet-updated')) { // 替换为Pokernow实际的下注金额DOM选择器 const betAmount = playerEl.querySelector('.player-bet-amount').textContent.trim(); // 发送消息到Node.js上下文 window.postMessage({ type: 'newBet', playerNum: idx + 1, amount: betAmount }); } }); }); // 配置观察选项,根据页面实际DOM更新逻辑调整 observer.observe(playerEl, { childList: true, attributes: true, subtree: true, attributeFilter: ['class'] }); }); });
方式2:用page.exposeFunction暴露Node.js函数到浏览器上下文
把Node.js环境的打印逻辑暴露给浏览器,在MutationObserver回调里直接调用(注意是异步调用)。
- 先暴露Node.js函数到浏览器:
// 将打印函数暴露给浏览器上下文 await page.exposeFunction('logNewBet', (playerNum, amount) => { console.log(`玩家 ${playerNum} 新下注金额:${amount}`); });
- 在浏览器上下文的MutationObserver回调中调用该函数:
await page.evaluate(async () => { const playerElements = document.querySelectorAll('.lobby-player'); playerElements.forEach(async (playerEl, idx) => { const observer = new MutationObserver(async (mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList' || mutation.target.classList.contains('bet-updated')) { const betAmount = playerEl.querySelector('.player-bet-amount').textContent.trim(); // 调用暴露的Node.js函数,必须用await处理异步逻辑 await window.logNewBet(idx + 1, betAmount); } } }); observer.observe(playerEl, { childList: true, attributes: true, subtree: true, attributeFilter: ['class'] }); }); });
注意事项
- 必须替换代码中的DOM选择器(如
.lobby-player、.player-bet-amount)为Pokernow网站实际的元素选择器,可通过浏览器开发者工具查看获取。 - MutationObserver的观察选项(如
childList、attributes)需要根据Pokernow更新下注时的DOM变化类型调整,若仅为文本变化,需添加characterData: true。
内容的提问来源于stack exchange,提问作者Rodney Deransburg
相关产品推荐
相关产品推荐

