Node.js爬虫报错:无法读取null的innerText属性求助
问题排查与修复方案
报错原因
TypeError: Cannot read properties of null (reading 'innerText') 说明代码中 document.querySelector 未找到目标元素,返回了null,后续读取innerText时触发错误。核心原因有两个:
- 页面结构变更:你使用的冗长硬编码选择器(比如
#main-content > div > section:nth-child(3) > div.css-gg4vpm...)依赖动态生成的CSS类名(如css-gg4vpm)和固定层级(如nth-child(3)),这类内容极易在网站更新时失效。 - 元素未完全加载:仅等待Cookie按钮不足以确保目标价格元素完成动态渲染,提前执行获取操作会导致找不到元素。
修复步骤
1. 替换为稳定的语义化选择器
放弃依赖动态类名和层级,改用文本内容定位元素(StockX的买卖按钮文本不会轻易变更):
修改pageScraper.js中的page.evaluate部分:
const result = await page.evaluate(() => { let demandes = ''; let offres = ''; // 遍历所有按钮,通过文本匹配目标元素 const allButtons = document.querySelectorAll('a.chakra-button, button.chakra-button'); allButtons.forEach(btn => { const pTag = btn.querySelector('p'); if (pTag && pTag.innerText.includes('Acheter')) { demandes = pTag.innerText; } else if (pTag && pTag.innerText.includes('Vendre')) { offres = pTag.innerText; } }); return { demandes, offres }; })
2. 添加元素加载等待逻辑
在执行元素获取前,确保目标内容已渲染完成:
// 在点击Cookie按钮后添加 await page.waitForFunction(() => { // 等待包含"Acheter"和"Vendre"的文本元素出现 return document.querySelector('p:contains("Acheter")') && document.querySelector('p:contains("Vendre")'); }, { timeout: 10000 }); // 超时时间设为10秒,避免无限等待
3. 完善错误处理与资源释放
避免单个元素找不到导致爬虫崩溃,同时规范浏览器资源关闭:
修改scraper函数:
async scraper(browser){ let page; try { page = await browser.newPage(); console.log(`Navigating to ${this.url}...`); // 等待网络空闲,确保页面静态资源加载完成 await page.goto(this.url, { waitUntil: 'networkidle2' }); await page.waitForSelector('#onetrust-reject-all-handler', { timeout: 5000 }); await page.click('#onetrust-reject-all-handler'); // 等待目标元素加载 await page.waitForFunction(() => { return document.querySelector('p:contains("Acheter")') && document.querySelector('p:contains("Vendre")'); }, { timeout: 10000 }); const result = await page.evaluate(() => { let demandes = ''; let offres = ''; const allButtons = document.querySelectorAll('a.chakra-button, button.chakra-button'); allButtons.forEach(btn => { const pTag = btn.querySelector('p'); if (pTag && pTag.innerText.includes('Acheter')) { demandes = pTag.innerText; } else if (pTag && pTag.innerText.includes('Vendre')) { offres = pTag.innerText; } }); return { demandes, offres }; }) console.log(result); return result; } catch (err) { console.error('爬虫执行出错:', err); return { demandes: null, offres: null }; } finally { // 先关闭页面再关闭浏览器,避免资源泄漏 if (page) await page.close(); if (browser) await browser.close(); } }
4. 优化文件写入逻辑
改用Promise风格的文件写入,和async/await语法统一:
修改pageController.js中的写入部分:
const scrapedData = await pageScraper.scraper(browser) try { // 格式化JSON输出,更易读 await fs.promises.writeFile("data.json", JSON.stringify(scrapedData, null, 2), 'utf8'); console.log("数据已抓取并保存成功!可查看 './data.json'"); } catch (err) { console.error('写入文件出错:', err); }
总结
核心问题是依赖不稳定的动态选择器且缺少元素加载等待。改用基于文本的语义化选择器,添加等待和错误处理后,爬虫会更健壮,能应对网站的小幅度更新。
内容的提问来源于stack exchange,提问作者Warrencky
相关产品推荐
相关产品推荐

