You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 14:48:18