调整Puppeteer自动滚动参数时出现TargetCloseError问题求助
动态滚动加载页面爬取时TargetCloseError问题排查与解决
问题描述
爬取一个向下滚动加载新元素的商品列表页面,编写的autoScroll函数能获取更多条目,但增大滚动延迟时间或滚动距离时,出现错误:
TargetCloseError: Protocol error (Runtime.callFunctionOn): Target closed
相关代码:
const puppeteer = require('puppeteer'); async function autoScroll(page) { await page.evaluate(async () => { await new Promise((resolve, reject) => { var totalHeight = 0; var distance = 100; var timer = setInterval(() => { var scrollHeight = document.body.scrollHeight; window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= scrollHeight) { clearInterval(timer); resolve(); } }, 100); }); }); } async function scrape() { const browser = await puppeteer.connect({ browserWSEndpoint: `wss://chrome.browserless.io` }); const page = await browser.newPage(); await page.goto('https://a201910091655256231033.szwego.com/static/index.html#/shop_detail/A201910091655256231033', {waitUntil: "networkidle0"}); await autoScroll(page); let combinedData = []; const textElements = await page.$$('.word-break'); const imageElements = await page.$$('.lazy'); for (let i = 0; i < textElements.length; i++) { const text = await page.evaluate(el => el.textContent.trim(), textElements[i]); let imageUrl = null; if (i < imageElements.length) { imageUrl = await page.evaluate(el => el.getAttribute('data-original').trim(), imageElements[i]); } combinedData.push({ text: text, imageUrl: imageUrl }); } console.log(JSON.stringify(combinedData, null, 2), combinedData.length); await browser.close(); } scrape().catch(console.error);
问题原因与解决办法
1. 远程浏览器连接超时
你使用的browserless.io远程浏览器服务存在连接超时限制,当page.evaluate中运行长时间异步逻辑(比如增大延迟后的滚动循环),会导致WebSocket连接断开,触发TargetCloseError。
解决方式:将滚动逻辑从浏览器端(page.evaluate内)移到Node端执行,拆分滚动步骤,每次滚动后等待页面稳定,避免长时间占用浏览器上下文。
2. 页面意外跳转或刷新
滚动过程中可能触发页面的某些行为(比如懒加载时的页面重定向、框架导航),导致当前页面目标被关闭。
解决方式:添加页面导航监听,排查是否有意外跳转:
page.on('framenavigated', (frame) => { console.log('页面跳转:', frame.url()); });
3. 优化滚动逻辑
重写autoScroll函数,在Node端控制滚动循环,每次滚动后等待元素加载或页面高度变化:
async function autoScroll(page) { let previousHeight = 0; while (true) { const currentHeight = await page.evaluate(() => document.body.scrollHeight); // 滚动到底部 await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); // 等待页面加载新内容,可根据页面调整等待时间或条件 await page.waitForTimeout(500); // 检查高度是否变化,无变化则停止滚动 if (currentHeight === previousHeight) break; previousHeight = currentHeight; } }
4. 增加连接稳定性配置
连接browserless时,可添加超时相关配置,延长连接存活时间:
const browser = await puppeteer.connect({ browserWSEndpoint: `wss://chrome.browserless.io`, defaultViewport: null, // 增加超时时间 timeout: 60000 });
修改后的完整代码示例
const puppeteer = require('puppeteer'); async function autoScroll(page) { let previousHeight = 0; while (true) { const currentHeight = await page.evaluate(() => document.body.scrollHeight); await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); // 根据页面加载速度调整等待时间 await page.waitForTimeout(500); if (currentHeight === previousHeight) break; previousHeight = currentHeight; } } async function scrape() { const browser = await puppeteer.connect({ browserWSEndpoint: `wss://chrome.browserless.io`, timeout: 60000 }); const page = await browser.newPage(); // 监听页面跳转,排查意外行为 page.on('framenavigated', (frame) => { console.log('页面导航:', frame.url()); }); await page.goto('https://a201910091655256231033.szwego.com/static/index.html#/shop_detail/A201910091655256231033', {waitUntil: "networkidle0"}); await autoScroll(page); let combinedData = []; const textElements = await page.$$('.word-break'); const imageElements = await page.$$('.lazy'); for (let i = 0; i < textElements.length; i++) { const text = await page.evaluate(el => el.textContent.trim(), textElements[i]); let imageUrl = null; if (i < imageElements.length) { imageUrl = await page.evaluate(el => el.getAttribute('data-original').trim(), imageElements[i]); } combinedData.push({ text: text, imageUrl: imageUrl }); } console.log(JSON.stringify(combinedData, null, 2), combinedData.length); await browser.close(); } scrape().catch(console.error);
内容的提问来源于stack exchange,提问作者liflaks
相关产品推荐
相关产品推荐

