Node.js环境下JSDOM获取元素innerText返回undefined问题
Node.js爬取Kijiji房源节点文本返回undefined排查方案
问题复现代码
import fetch from 'node-fetch'; import { JSDOM } from 'jsdom'; import {Appartment} from "./models/Appartment.mjs" let applist = [] let multipleDivs = [] async function kijAppartments() { try { const kijCall = await fetch(`https://www.kijiji.ca/b-ville-de-montreal/appartement-4-1-2/k0l1700281?rb=true&dc=true`); if(!kijCall.ok) { throw new Error(`HTTP error: ${kijCall.status}`) } const response = await kijCall.text() const dom = new JSDOM(response) multipleDivs = dom.window.document.querySelectorAll(".info-container") return multipleDivs } catch(error) { console.log("Error Made") console.log(error) } } async function arrayOfApps() { await kijAppartments() .then(data => { data.forEach(div => { const newApp = new Appartment newApp.price = div.childNodes[1].innerText newApp.title = div.childNodes[3].innerText newApp.description = div.childNodes[7].innerText console.log(newApp) }) }) } await arrayOfApps()
问题现象
浏览器访问目标页面时,控制台执行const aList = document.querySelectorAll(".info-container")可正常获取所有匹配节点,节点的innerHTML、innerText属性均能返回实际文本值;但Node.js环境运行上述代码时,生成的Appartment实例所有属性值均为undefined,无法读取节点文本内容。
排查解决步骤
- 先校验fetch返回的页面内容是否正常
Kijiji有基础反爬机制,默认的node-fetch请求头带的User-Agent会被识别为爬虫,返回验证页、空白页或者结构和浏览器端不一致的简化页面。先把kijCall.text()拿到的响应内容打印或者存为本地HTML文件打开,确认是否是正常的房源列表页。如果是反爬拦截的页面,给fetch添加浏览器标识请求头:const kijCall = await fetch(`https://www.kijiji.ca/b-ville-de-montreal/appartement-4-1-2/k0l1700281?rb=true&dc=true`, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } }); - 废弃硬编码
childNodes索引的节点读取方式childNodes返回的节点列表会包含所有节点类型:换行、空格生成的空文本节点、注释节点、实际元素节点,不同环境下HTML源码的格式化空白、注释内容存在差异,固定取第1、3、7个子节点的逻辑必然会出现索引错位,读不到目标元素。 - 替换节点读取逻辑
不要靠数节点索引取值,在.info-container容器范围内用选择器精准匹配目标字段,同时注意JSDOM对innerText的实现和浏览器存在差异,优先用标准DOM属性textContent读取文本,读取后调用trim()清除首尾多余空白:data.forEach(div => { const newApp = new Appartment() // 匹配字段时以实际返回的DOM结构的类名为准,不要硬编码索引 newApp.price = div.querySelector('.price')?.textContent?.trim() newApp.title = div.querySelector('.title a')?.textContent?.trim() newApp.description = div.querySelector('.description')?.textContent?.trim() console.log(newApp) }) - 结构校验
如果选择器匹配不到值,先在循环内打印div.innerHTML查看当前容器下的实际DOM结构,再对应调整选择器即可,不要直接照搬浏览器DevTools里看到的节点索引。
内容的提问来源于stack exchange,提问作者mrnanse
相关产品推荐
相关产品推荐

