寻求轻量Node.js DOM加载库解决递归异步脚本加载页面的爬取问题
寻求轻量Node.js DOM加载库解决递归异步脚本加载页面的爬取问题
这种异步递归加载数据的页面确实挺棘手的,尤其是还要兼顾爬取效率,没法用Puppeteer、Selenium这类重型浏览器自动化工具的时候。我来给你几个实用的思路,看看能不能解决你的问题:
一、先排查jsdom的异步等待逻辑
你当前的代码可能犯了一个常见的错误:还没等页面所有异步资源加载完成,就去查询DOM元素了。jsdom的脚本执行和资源加载是异步的,直接初始化完就调用querySelectorAll,元素肯定还没渲染出来。
你可以试试监听页面的load事件,或者用Promise等待所有异步操作完成后再查询元素,比如:
const JSDOM = require('jsdom').JSDOM; async function scrapePage(p_ind) { const dom = new JSDOM(``, { url: `https://www.pnp.co.za/c/pnpbase?currentPage=${p_ind.toString()}`, contentType: `text/html`, referrer: `https://pnp.co.za/`, includeNodeLocations: true, resources: `usable`, storageQuota: 10000000, pretendToBeVisual: true, // 加上真实的User-Agent,避免被反爬拦截 userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' }); // 等待页面所有资源加载完成 await new Promise(resolve => dom.window.addEventListener('load', resolve)); // 现在再查询元素 const productElements = dom.window.document.querySelectorAll('div[class~="product-action"]'); console.log(`找到的产品元素数量:${productElements.length}`); } scrapePage(1); // 替换成你的页码
另外,还要注意有些页面的异步脚本可能依赖特定的全局变量,或者有跨域限制(不过你已经设置了正确的url,同源问题应该不大),可以打开jsdom的调试日志看看有没有资源加载失败的情况。
二、更高效的方案:直接抓取后端API
其实对于这类动态加载的页面,直接调用后端提供的数据接口比用DOM库执行脚本要高效得多,也更稳定。你可以按这个步骤操作:
- 打开Chrome浏览器的开发者工具(F12),切换到「Network」面板,筛选「XHR/Fetch」类型的请求;
- 手动翻页,观察哪些请求返回了产品列表数据;
- 复制这些请求的URL、请求头(尤其是Cookie、User-Agent、Referer这些字段),然后用
axios或者node-fetch直接模拟请求。
这种方法跳过了前端渲染的过程,速度快得多,也不用处理复杂的异步脚本加载问题,非常适合大量页面的爬取。
三、换用更贴近浏览器的轻量DOM库
如果一定要用DOM执行脚本的方案,可以试试HappyDOM——它对浏览器环境的模拟比jsdom更彻底,对异步递归脚本的支持也更好。示例代码如下:
const { Window } = require('happy-dom'); async function scrapeWithHappyDOM(p_ind) { const window = new Window({ url: `https://www.pnp.co.za/c/pnpbase?currentPage=${p_ind.toString()}`, referrer: `https://pnp.co.za/`, userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' }); // 等待所有异步操作(脚本加载、XHR请求)完成 await window.happyDOM.whenAsyncComplete(); const productElements = window.document.querySelectorAll('div[class~="product-action"]'); console.log(`找到的产品元素数量:${productElements.length}`); } scrapeWithHappyDOM(1);
额外注意点
- 检查目标网站的反爬机制:有些网站会检测请求的User-Agent、Cookie,甚至会限制请求频率,记得设置合理的请求间隔,避免被封IP;
- 如果页面有动态生成的Cookie,可能需要先请求一次首页获取Cookie,再带着Cookie去请求列表页。
备注:内容来源于stack exchange,提问作者Ethan Nichols
相关产品推荐
相关产品推荐

