使用Puppeteer提取多层div嵌套h4文本失败,程序异常终止求助
问题:Puppeteer爬取星球状态时元素数量不稳定
问题详情
我用Puppeteer爬取https://play.projectnebula.com/planet/[ID]系列页面,目标是提取嵌套在7层div下的h4文本(内容为"Claimed"),以此筛选未认领的星球(所有状态均通过h4标签展示)。但运行代码时,前1-2个URL能获取到全部13个h4元素,后续URL仅能拿到11个,导致无法定位到对应"Claimed"的h4,程序提前终止。
原代码
const puppeteer = require('puppeteer') async function run() { const browser = await puppeteer.launch({ headless: false, ignoreHTTPSErrors: true, }) var x = 1101; while (x !== 0) { const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (req) => { if (req.resourceType() == 'image' || req.resourceType() == 'font') { req.abort(); } else { req.continue(); } }); page.setDefaultTimeout(0); await page.goto(`https://play.projectnebula.com/planet/${x}`); await page.waitForSelector('h4'); const elements = await page.$$("h4"); let text = await (await elements[elements.length - 2].getProperty("innerText")).jsonValue() text = text.toLowerCase().trim(); if (text == 'claimed' || text == 'on sale') { console.log(text, x) x += 1; await page.close(); } else { console.log(x, text) x = 0 } } } run();
问题原因分析
- 等待时机过早:仅等待第一个h4出现就开始获取元素,页面后续的h4可能还未渲染完成,导致元素数量不足
- 页面复用问题:每次循环新建页面,可能存在浏览器资源分配不稳定、页面加载状态不一致的情况
- 元素定位脆弱:依赖
elements.length -2的索引定位状态h4,完全依赖页面h4的数量排序,一旦页面结构微调就会失效 - 请求拦截的潜在影响:虽然拦截了图片和字体,但可能某些关键请求的加载状态未被正确等待
修复方案
核心改进点
- 用精准选择器直接定位目标状态h4(基于7层div的层级),避免依赖索引
- 等待页面完全加载完成,确保所有h4都渲染完毕
- 复用同一个页面,减少资源开销和加载波动
修复后的代码
const puppeteer = require('puppeteer') async function run() { const browser = await puppeteer.launch({ headless: false, ignoreHTTPSErrors: true, }) // 复用同一个页面,减少资源消耗 const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (req) => { if (req.resourceType() === 'image' || req.resourceType() === 'font') { req.abort(); } else { req.continue(); } }); page.setDefaultTimeout(0); let x = 1101; while (x !== 0) { try { // 等待页面完全加载(networkidle2表示仅剩下2个以下的网络请求) await page.goto(`https://play.projectnebula.com/planet/${x}`, { waitUntil: 'networkidle2' }); // 用精准层级选择器定位7层div下的h4,若有父级类名可替换为更精准的选择器 await page.waitForSelector('div > div > div > div > div > div > div h4', { timeout: 10000 }); // 直接获取目标h4文本,避免依赖索引 const statusText = await page.$eval('div > div > div > div > div > div > div h4', el => el.innerText.toLowerCase().trim()); if (statusText === 'claimed' || statusText === 'on sale') { console.log(statusText, x); x += 1; } else { console.log(x, statusText); x = 0; } } catch (err) { // 处理超时或元素未找到的情况,避免程序崩溃 console.error(`处理星球${x}时出错:`, err.message); x += 1; } } await page.close(); await browser.close(); } run();
额外优化建议
- 如果知道目标h4所在父级div的类名,把层级选择器替换成带类名的版本(比如
.planet-status-container h4),稳定性会更高 - 若页面存在懒加载,可增加页面滚动操作确保所有元素渲染完成
- 加入随机延迟,降低被网站反爬机制拦截的风险
内容的提问来源于stack exchange,提问作者Hafeez Ali
相关产品推荐
相关产品推荐

