使用Puppeteer爬取网站返回空数组问题求助
问题分析与修复方案
你的代码返回空数组的核心原因是依赖固定nth-child索引定位元素,但移除广告后DOM结构发生偏移,导致后续元素无法被正确匹配,同时还有多处细节问题阻碍数据收集:
具体问题点
- 广告移除后索引偏移:删除第22、43位元素后,后续元素的索引会自动前移,原循环
i从3到53的固定索引逻辑会跳过大量有效元素,甚至触发waitForSelector超时。 - 无效重复判断:两次重复检查
div:nth-child(22)是否存在,且广告移除后该元素已不存在,直接导致循环跳过数据收集逻辑。 - 弹窗点击无容错:部分页面可能没有弹出广告,直接执行
page.click会抛出错误,中断整个爬取流程。 - 固定延时不可靠:
waitForTimeout(3000)无法保证页面完全加载,容易出现元素未渲染完成就执行后续操作的情况。 - 低效的元素遍历:通过循环索引定位元素的方式极易受DOM结构变化影响,稳定性极差。
修复后的代码
const puppeteer = require("puppeteer"); const express = require("express"); const cors = require("cors"); const app = express(); app.use(cors()); let data = []; (async () => { const browser = await puppeteer.launch({ headless: false, defaultViewport: null, }); const page = await browser.newPage(); // 弹窗关闭容错逻辑:先判断存在再操作 const closePopup = async () => { const popupBtn = await page.$("#promotionalPopup button"); if (popupBtn) { await popupBtn.click(); await page.waitForTimeout(500); } }; for (let pageNumber = 1; pageNumber < 42; pageNumber++) { try { await page.goto(`https://naamhinaam.com/baby-girl-names-a?page=${pageNumber}`, { waitUntil: "networkidle2" // 等待网络空闲,确保页面加载完成 }); await closePopup(); // 移除广告元素:先判断存在再删除,避免报错 const ad1 = await page.$("div.name-suggestion.mt-1 > div > div:nth-child(22)"); if (ad1) await page.evaluate(el => el.remove(), ad1); const ad2 = await page.$("div.name-suggestion.mt-1 > div > div:nth-child(43)"); if (ad2) await page.evaluate(el => el.remove(), ad2); // 直接获取所有目标元素,摆脱固定索引依赖 await page.waitForSelector("div.name-suggestion.mt-1 div.nsg__name_meaning a"); const nameElements = await page.$$("div.name-suggestion.mt-1 div.nsg__name_meaning a"); // 遍历元素收集数据 for (const el of nameElements) { const fullName = await page.evaluate(el => el.textContent.trim(), el); if (fullName) { // 过滤空值 data.push({ fullName }); } } console.log(`第${pageNumber}页已收集,当前总数据量:${data.length}`); } catch (err) { console.error(`第${pageNumber}页爬取失败:`, err); } } await browser.close(); })(); app.get("/", (req, res) => { res.status(200).json(data); }); app.listen(3000, () => { console.log("App is running on port 3000..."); });
关键优化点
- 容错处理弹窗与广告:先判断元素是否存在再执行操作,避免因元素缺失导致的流程中断。
- 放弃固定索引定位:直接通过目标元素的特征选择器获取所有元素,不受DOM结构变化影响。
- 可靠的页面加载等待:使用
waitUntil: "networkidle2"替代固定延时,确保页面资源加载完成。 - 错误捕获机制:添加
try/catch处理单页爬取失败的情况,避免某一页出错导致整个爬取终止。 - 数据过滤:添加空值过滤,避免无效数据进入结果数组。
内容的提问来源于stack exchange,提问作者Konidoni Ravirr
相关产品推荐
相关产品推荐

