如何使用jsdom抓取页面所有脚本执行完成后的完整HTML内容
解决方法
你当前的代码在JSDOM加载完初始HTML后就立即读取DOM,没有等待页面内的异步脚本、接口请求、渲染逻辑执行完成,因此只能拿到未渲染的初始内容,可通过以下两种方案解决:
方案1:修改现有jsdom逻辑,增加等待机制
通过监听页面加载完成事件+额外等待异步逻辑执行的方式,适配简单动态页面场景:
const fs = require('fs'); const jsdom = require('jsdom'); const { JSDOM } = jsdom; const pdfUrl = 'https://cma.org.sa/Market/imf/Pages/default.aspx'; // 封装延时等待函数 function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function loadListView(){ const dom = await JSDOM.fromURL(pdfUrl, { includeNodeLocations: true, pretendToBeVisual: true, runScripts: 'dangerously', resources: "usable", }); // 先等待页面基础load事件触发,确认静态资源加载完成 await new Promise(resolve => { dom.window.addEventListener('load', resolve); }); // 额外等待3秒保障异步脚本、接口请求执行完成,时长可根据实际页面情况调整 await wait(3000); // 此时获取的就是脚本执行完成后的完整HTML const fullRenderedHtml = dom.window.document.documentElement.outerHTML; console.log(fullRenderedHtml); dom.window.document.querySelectorAll('a').forEach(link => { console.log(link.href); }); return dom; } loadListView()
该方案局限性:如果页面存在jsdom不兼容的浏览器API、高延时异步逻辑、ASP.NET回发交互等场景,可能还是无法拿到完整渲染内容,推荐使用方案2。
方案2:使用Puppeteer无头浏览器实现(兼容性最好)
Puppeteer是基于Chromium的无头浏览器,运行环境和真实浏览器完全一致,对复杂动态页面的适配性远高于jsdom。
首先安装依赖:npm i puppeteer
参考代码:
const puppeteer = require('puppeteer'); const pdfUrl = 'https://cma.org.sa/Market/imf/Pages/default.aspx'; async function loadListView() { // 启动无头浏览器 const browser = await puppeteer.launch({ headless: 'new' }); const page = await browser.newPage(); // 访问页面,等待网络空闲后再继续,保障所有异步请求完成 await page.goto(pdfUrl, { waitUntil: 'networkidle2' }); // 可选配置:等待你需要的目标元素渲染完成,把下面的选择器替换为页面实际元素的CSS选择器即可 // await page.waitForSelector('目标元素CSS选择器', { timeout: 10000 }); // 获取完整渲染后的HTML const fullRenderedHtml = await page.content(); console.log(fullRenderedHtml); // 提取所有链接 const links = await page.evaluate(() => { return Array.from(document.querySelectorAll('a')).map(link => link.href); }); console.log(links); await browser.close(); } loadListView();
内容的提问来源于stack exchange,提问作者dev-ninja
相关产品推荐
相关产品推荐

