如何使用Puppeteer监听指定span元素的文本内容变化
Hey there! Let's tackle your problem step by step. The issues you're facing come down to two key points: passing non-serializable values to waitForFunction (which causes the circular structure error) and using a static condition that doesn't fit your need to track any status change. Here's how to fix this and achieve your goal of logging every status update with timestamps:
Option 1: Use MutationObserver (Recommended)
This is the most efficient and reliable way to track text changes in an element, as it listens directly for DOM mutations instead of polling the element repeatedly.
const page = await browser.newPage(); await page.goto('https://chat.com'); // Get initial status and log it let currentStatus = await page.$eval('#status', el => el.innerText.trim()); console.log(`[${new Date().toISOString()}] Initial status: ${currentStatus}`); // Expose a function to the browser context to log status changes await page.exposeFunction('logStatusChange', (newStatus) => { console.log(`[${new Date().toISOString()}] Status updated to: ${newStatus}`); currentStatus = newStatus; }); // Set up MutationObserver in the browser context to watch for text changes await page.evaluate(() => { const statusElement = document.querySelector('#status'); // Store the last seen status to avoid duplicate logs window.lastRecordedStatus = statusElement.innerText.trim(); const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { // Check if the change affects the element's text content if (mutation.type === 'characterData' || mutation.type === 'childList') { const updatedStatus = statusElement.innerText.trim(); if (updatedStatus !== window.lastRecordedStatus) { window.lastRecordedStatus = updatedStatus; logStatusChange(updatedStatus); } } } }); // Configure observer to watch for text-related changes observer.observe(statusElement, { characterData: true, childList: true, subtree: true // Required if text is nested inside child elements }); }); // Keep the page open to continue listening (adjust timeout as needed) await page.waitForTimeout(3600000); // Listen for 1 hour
Option 2: Poll with waitForFunction
If you prefer a simpler approach (or if MutationObserver doesn't work for your specific DOM structure), you can use a loop with waitForFunction to check for status changes.
const page = await browser.newPage(); await page.goto('https://chat.com'); let currentStatus = await page.$eval('#status', el => el.innerText.trim()); console.log(`[${new Date().toISOString()}] Initial status: ${currentStatus}`); // Loop indefinitely to track changes while (true) { // Wait until the status differs from the current value await page.waitForFunction( (currentVal) => document.querySelector('#status').innerText.trim() !== currentVal, {}, // Optional: Add timeout options here if needed currentStatus // Pass current status as a serializable parameter ); // Fetch and log the new status currentStatus = await page.$eval('#status', el => el.innerText.trim()); console.log(`[${new Date().toISOString()}] Status updated to: ${currentStatus}`); }
Why Your Original Code Failed
- Circular Structure Error: You tried passing
statusHandle(an ElementHandle) towaitForFunction. ElementHandles are Node.js-side objects with circular references, which can't be serialized and sent to the browser context. Always use primitive values (like strings) as parameters forwaitForFunction. - Static Condition: Your simplified
waitForFunctionchecked for an exact match to "Online"—if the status never changed to that value, the function would hang indefinitely. Instead, you need to wait for the status to differ from the current known value.
内容的提问来源于stack exchange,提问作者denis

