新闻中心
JSDOM查询NodeList为空:动态内容抓取策略与Puppeteer实践

在使用JSDOM和Axios进行网页抓取时,有时会遇到`querySelectorAll`返回空`NodeList`的问题,尤其当目标元素(如`
网页抓取中NodeList为空的常见问题
在使用axios获取网页HTML内容,并结合JSDOM解析DOM时,开发者可能会遇到一个令人困惑的现象:通过querySelector或querySelectorAll查询某个元素(例如ul下的li元素),尽管在浏览器开发者工具中这些元素清晰可见,但在代码中返回的NodeList却是空的,或者其length属性为0。
例如,针对以下HTML结构:
<div id="download-options">
<div class="panel-body">
<ul>
<li><a href="url1"></a></li>
<li><a href="url2"></a></li>
<li><a href="url3"></a></li>
</ul>
</div>
</div>尝试使用以下JSDOM代码抓取li元素:
// 假设 res.data 包含了上述HTML
const dom = new JSDOM(res.data);
const ulist = dom.window.document.querySelector('#download-options > .panel-body > ul');
// ulist 可能会正确返回 HTMLUListElement {}
// ulist.childElementCount 可能会返回 1 (表示有一个子元素,即文本节点或注释)
const lists = ulist.querySelectorAll('li');
// 此时 lists.length 却返回 0,而非预期的 3这种情况下,ulist本身可能被正确选中,但其内部的li元素却无法通过querySelectorAll获取。
问题根源:静态抓取与动态渲染
这个问题的核心在于网页内容的加载方式。axios作为一个HTTP客户端,它只能获取服务器首次响应的原始HTML字符串。JSDOM则是一个纯J*aScript的DOM实现,它根据这个原始HTML字符串构建DOM树,但它不具备浏览器环境,无法执行页面中的J*aScript代码。
Kreado AI
Kreado AI是一个多语言AI视频创作平台,只需输入文本或关键词,即可创作真实/虚拟人物的多语言口播视频。 为创作者提供AI赋能
182
查看详情
许多现代网站会利用J*aScript在页面加载完成后动态地生成、修改或插入DOM元素。这些动态内容可能包括:
- 通过AJAX请求获取数据后渲染列表项。
- 基于用户行为或设备状态调整页面布局。
- 通过前端框架(如React, Vue, Angular)进行客户端渲染。
- 网站可能出于反爬虫目的,延迟加载关键内容,或依赖于特定的Cookie、Session或用户代理来显示完整内容。
当axios获取到的是一个“骨架屏”HTML,而li元素是由后续J*aScript代码动态填充时,JSDOM处理的DOM树自然就不会包含这些动态生成的li元素,从而导致querySelectorAll返回空结果。在浏览器开发者工具中能看到这些元素,是因为浏览器完整执行了所有J*aScript,渲染出了最终的DOM。
解决方案:使用Puppeteer处理动态内容
为了解决JSDOM无法处理动态内容的问题,我们需要一个能够模拟真实浏览器环境的工具。Puppeteer是一个由Google开发的Node.js库,它提供了一套高级API来通过DevTools协议控制Chrome或Chromium浏览器。这意味着Puppeteer能够:
- 加载网页并执行所有J*aScript。
- 等待异步内容加载完成。
- 模拟用户交互(点击、输入等)。
- 截取屏幕截图或生成PDF。
- 获取最终渲染的DOM内容。
Puppeteer实践示例
以下代码展示了如何使用Puppeteer来抓取动态加载的li元素:
const puppeteer = require('puppeteer');
(async () => {
// 1. 启动无头浏览器实例
// headless: true 表示在后台运行浏览器,不显示UI界面
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// 2. 访问网站首页 (可选,但可能解决某些网站的缓存/Cookie问题)
// 某些网站会根据首次访问设置Cookie或Session,影响后续页面的内容
await page.goto('http://example.com', { waitUntil: 'domcontentloaded' }); // 等待DOM加载完成
// 3. 访问目标页面
await page.goto('https://example.com/targetpage', { waitUntil: 'networkidle2' });
// waitUntil: 'networkidle2' 会等待网络连接空闲2秒,通常意味着页面所有资源已加载完毕
// 也可以使用 'domcontentloaded' 或 'load',具体取决于页面加载特性
// 4. 等待目标元素出现
// 这一步至关重要,确保动态加载的元素已经渲染到DOM中
await page.waitForSelector('#download-options li', { timeout: 5000 }); // 最多等待5秒
// 5. 查询元素并提取数据
// page.$ 用于查询单个元素
const ul = await page.$("#download-options ul");
// page.$$ 用于查询多个元素
const lis = await ul.$$("li");
console.log(`找到 ${lis.length} 个 li 元素。`);
// 6. 遍历并提取每个 li 内部的 a 标签的 href 属性
for await (const li of lis) {
const a = await li.$('a'); // 在每个 li 元素内部查询 a 标签
if (a
) {
// 使用 evaluate 方法在浏览器环境中执行JS代码来获取属性值
const hrefValue = await a.evaluate((el) => el.getAttribute('href'));
console.log(`链接地址: ${hrefValue}`);
}
}
// 7. 关闭浏览器实例
await browser.close();
})();代码解析与注意事项
- puppeteer.launch({ headless: true }): 启动一个Chromium实例。headless: true表示无头模式,浏览器在后台运行,没有图形界面。在开发调试阶段,可以设置为false来观察浏览器行为。
-
page.goto(url, { waitUntil: 'networkidle2' }): 导航到指定URL。waitUntil参数非常重要,它决定了Puppeteer何时认为页面加载完成。
- domcontentloaded: DOMContentLoaded事件触发时。
- load: load事件触发时。
- networkidle0: 当网络连接数变为0后持续至少500ms。
- networkidle2: 当网络连接数变为2后持续至少500ms。 选择合适的waitUntil策略对于确保所有动态内容加载至关重要。networkidle2通常是一个比较稳妥的选择。
- page.waitForSelector('#download-options li', { timeout: 5000 }): 这是解决动态内容问题的关键。它会暂停代码执行,直到指定的CSS选择器对应的元素出现在DOM中。如果元素在timeout时间内未出现,则会抛出错误。这确保了在尝试查询元素之前,它们已经被J*aScript渲染到页面上。
-
page.$ 和 page.$$:
- page.$(selector): 类似于document.querySelector(),返回匹配的第一个ElementHandle对象。
- page.$$(selector): 类似于document.querySelectorAll(),返回所有匹配的ElementHandle对象数组。 ElementHandle是Puppeteer对DOM元素的引用,它允许你在其上执行进一步的操作,如查询子元素或调用evaluate。
- element.evaluate((el) => el.getAttribute('href')): evaluate方法允许你在浏览器上下文(即页面内部)执行一段J*aScript代码。这对于获取元素的属性、文本内容或执行更复杂的DOM操作非常有用,因为这些操作是在真实的浏览器环境中进行的。
- 资源管理: 务必在抓取完成后调用browser.close()来关闭浏览器实例,释放系统资源。
总结
在进行网页抓取时,理解目标网站的内容加载机制至关重要。对于静态HTML内容,axios结合JSDOM是一个轻量且高效的选择。然而,一旦遇到由J*aScript动态渲染的内容,JSDOM就力不从心了。此时,Puppeteer作为无头浏览器自动化工具,能够完美模拟真实用户的浏览行为,执行页面J*aScript,并等待动态内容加载完成,从而成功抓取到完整的DOM结构和数据。正确选择工具并熟练运用page.waitForSelector等等待机制,是成功抓取动态网页内容的关键。
以上就是JSDOM查询NodeList为空:动态内容抓取策略与Puppeteer实践的详细内容,更多请关注其它相关文章!
# vue
# react
# javascript
# java
# html
# js
# 前端
# node.js
# css
# 选择器
# 晋源区seo优化建议
# 六安网站建设及推广公司
# 全民学习网站建设
# seo优化的基本思路
# 汉川网站推广排名哪家好
# 怎么推广网站运营
# 南宁好的网站推广平台
# 开江网站推广哪家好点
# seo快速优化软件小学
# XX网站建设路推荐
# 你在
# 首次
# 是因为
# 至关重要
# 无头
# 为空
# 是一个
# 加载
# 关键词
# g
# node
# ajax
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
CSS Flexbox与媒体查询:实现响应式布局中元素的并排与堆叠
Win11怎么设置鼠标指针速度_Win11提高鼠标指针精确度选项
豆包手机助手发布技术预览版:直接嵌入手机系统!努比亚样机发售
AO3网页版最新入口合集 Archive of Our Own在线访问指南
钉钉视频会议声音异常如何处理 钉钉会议音频修复技巧
msn官网入口地址手机版 msn官方网站手机最新链接
J*a里如何实现订单支付与库存同步功能_支付库存同步项目开发方法说明
Win10怎么设置静态IP地址 Win10手动配置IP地址步骤【指南】
今日头条怎么同步内容到抖音_今日头条内容同步到抖音教程
在Go语言中利用后缀数组处理多字符串:实现高效文本匹配与自动补全
在J*a中如何开发简易电子商务商品管理系统_商品管理系统项目实战解析
QQ邮箱在线登录平台 QQ邮箱个人邮箱网页版入口
J*aScript数据结构转换:将对象数组按类别分组
excel怎么制作工资条 excel快速生成工资条的方法
蛙漫2台版漫画地址 Manwa2正版网页版链接
漫蛙官网正版漫画入口 漫蛙2官方网页登录地址
Selenium Python中处理点击后新窗口加载冻结问题的策略与实践
淘宝支付提示失败如何解决 淘宝支付流程优化方法
Composer的 archive 命令怎么用_快速打包你的PHP项目及其Composer依赖
NVIDIA股价11月重挫12%:下月有望好转 但难回5万亿美元巅峰
Yandex官网搜索引擎免登录_俄罗斯Yandex一键直达入口
WordPress插件开发:正确注册卸载钩子与避免常见陷阱
CSS响应式网页如何实现主次模块比例自适应_flex-grow与flex-shrink调整
Bilibili动漫最新防封地址发布-Bilibili动漫2025年最稳正版入口推荐
J*aScript实现动态背景色下的文本与按钮颜色自适应调整
jQuery Mask 插件中实现电话号码固定前导零的教程
b站怎么删除评论_b站评论管理与删除操作
Yandex搜索引擎官方地址 俄罗斯网络世界的主要入口
windows10怎么查看本机ip_windows10命令提示符ipconfig使用
一加 14R 快充无反应_一加 14R 充电优化
如何在CSS中使用visited与link控制链接颜色_visited link伪类配合
AWS EC2实例间SQL Server连接超时:安全组配置与故障排除指南
抓大鹅解压小游戏 抓大鹅摸鱼解压入口
PDF文件体积过大处理_PDF压缩技巧详解
腾讯视频怎么举报不良内容_腾讯视频内容举报流程与违规信息处理方法
J*a编写用户注册与登录功能_掌握字符串与验证逻辑
css元素hover动画延迟生效怎么办_使用animation-delay调整触发时间
圆通快递查询实时追踪 圆通物流包裹状态快速查看
Basecamp怎样用留言钉固定重点_Basecamp用留言钉固定重点【重点标记】
理解Python模块与全局变量的作用域管理
c++如何使用std::memory_order控制原子操作顺序_c++ C++11内存模型详解
Yandex搜索引擎一键访问入口_俄罗斯Yandex官网免登录
漫蛙Manwa2官网入口地址分享 漫蛙漫画PC版永久访问通道
Spring Boot嵌入式服务器与J*a EE:功能支持深度解析
使用Python高效删除Word宏并转换DOCM为DOCX格式
拼多多视频播放卡顿如何处理 拼多多视频播放优化技巧
淘宝网网页版登录入口 淘宝官方网页版快捷登录
电脑安装程序提示“错误1722”怎么办_Windows Installer服务问题解决【教程】
谷歌邮箱网页版官方页面入口 谷歌邮箱网页端快速访问
ArchiveofOurOwn小说阅读-ArchiveofOurOwn同人作品访问链接


2025-11-19
浏览次数:次
返回列表
) {
// 使用 evaluate 方法在浏览器环境中执行JS代码来获取属性值
const hrefValue = await a.evaluate((el) => el.getAttribute('href'));
console.log(`链接地址: ${hrefValue}`);
}
}
// 7. 关闭浏览器实例
await browser.close();
})();