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

使用cheerio和axios爬取URL不变的分页页面获取不同年份总收益率咨询

网页爬取动态分页(URL无变化)实现方案

核心问题说明

你当前只能拿到默认年限数据的原因是:该站点切换年限属于前端动态交互场景,不会触发整页刷新,URL也不会更新,axios的静态GET请求只能拿到初始加载的默认年限HTML,cheerio无法处理动态交互后的内容更新。

方案1:抓取异步接口(优先推荐,性能更高)

这种站点切换年限时一般会向后端发送异步请求拉取对应数据,你可以直接模拟这些请求拿数据,不用解析HTML,效率更高:

  1. 操作步骤:
  • 打开浏览器F12调试工具,切换到「网络」面板,筛选「Fetch/XHR」
  • 手动切换不同年限选项,找到对应的数据请求,记录请求地址、请求方法、请求参数、请求头
  • 用axios循环模拟不同年限参数的请求即可,部分接口返回结构化JSON,可直接提取数据,若返回HTML片段复用你原有的cheerio解析逻辑即可
  1. 示例代码逻辑:
const axios = require("axios");
const cheerio = require("cheerio");
const http = require("http");

// 从调试工具中获取到的所有可选年限参数,根据站点实际情况调整
const TIME_RANGES = ['1y', '3y', '5y', '10y'];
// 你抓到的真实数据接口地址
const API_URL = 'https://www.imoney.my/your-captured-api-path';

// 数据解析逻辑复用你原有代码
function parseFunds(html) {
  const $ = cheerio.load(html);
  const funds = [];
  $(".list-item").each((_i, row) => {
    const $row = $(row);
    const fund = $row.find("[class*='product-title'] a").text().trim();
    const price = $row.find("[class*='is-narrow product-profit'] b").text().trim();
    const risk = $row.find("[class*='product-title'] [class*='font-xsm extra-info']").text().replace('/10','').trim();
    const totalreturn = $row.find("[class*='product-return'] .font-lg b").text().replace('%','').trim();
    funds.push({ fund, price, risk, totalreturn });
  });
  return funds;
}

http
  .createServer(async function (_, res) {
    try {
      const allData = {};
      // 第一次请求首页拿cookie、csrf token等必要参数
      const baseRes = await axios.get('https://www.imoney.my/unit-trust-investments', {
        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'
        }
      });
      // 若有csrf token可以从baseRes.data里提取,放到后续请求头中

      for (const range of TIME_RANGES) {
        const apiRes = await axios.post(API_URL, {
          // 替换为你抓到的真实请求参数
          time_range: range,
          page: 1
        }, {
          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',
            'X-Requested-With': 'XMLHttpRequest',
            // 若需要cookie直接用baseRes.headers['set-cookie']带上
            'Cookie': baseRes.headers['set-cookie']?.join('; ')
          }
        });
        // 若接口返回JSON直接提取,返回HTML则调用parseFunds解析
        allData[range] = parseFunds(apiRes.data);
      }

      res.statusCode = 200;
      res.write(JSON.stringify(allData, null, 4));
    } catch (err) {
      console.error(err);
      res.statusCode = 400;
      res.write("Unable to process request.");
    }
    res.end();
  })
  .listen(8080);

方案2:无头浏览器模拟交互(适配复杂加密场景)

如果接口有加密、参数不好破解,直接用puppeteer模拟真实浏览器操作,点击切换年限后提取页面数据即可:

  1. 先安装依赖:
    npm install puppeteer
  2. 示例代码:
const puppeteer = require('puppeteer');
const http = require("http");

http
  .createServer(async function (_, res) {
    try {
      // 启动无头浏览器
      const browser = await puppeteer.launch({ headless: 'new' });
      const page = await browser.newPage();
      // 访问目标页面,等待网络空闲
      await page.goto('https://www.imoney.my/unit-trust-investments', { waitUntil: 'networkidle2' });

      const allData = {};
      // 获取所有年限选项的标识,替换为页面上实际的年限按钮选择器
      const timeOptions = await page.$$eval('.time-filter button', btns => 
        btns.map(btn => ({ value: btn.value, text: btn.textContent.trim() }))
      );

      for (const option of timeOptions) {
        // 点击对应年限按钮
        await page.click(`.time-filter button[value="${option.value}"]`);
        // 等待收益率数据加载完成
        await page.waitForSelector('.list-item [class*="product-return"] .font-lg b', { timeout: 5000 });
        // 提取当前页面的基金数据
        const funds = await page.evaluate(() => {
          const list = [];
          document.querySelectorAll('.list-item').forEach(row => {
            const fund = row.querySelector("[class*='product-title'] a")?.textContent.trim() || '';
            const price = row.querySelector("[class*='is-narrow product-profit'] b")?.textContent.trim() || '';
            const risk = row.querySelector("[class*='product-title'] [class*='font-xsm extra-info']")?.textContent.replace('/10','').trim() || '';
            const totalreturn = row.querySelector("[class*='product-return'] .font-lg b")?.textContent.replace('%','').trim() || '';
            list.push({ fund, price, risk, totalreturn });
          });
          return list;
        });
        allData[option.text] = funds;
      }

      await browser.close();
      res.statusCode = 200;
      res.write(JSON.stringify(allData, null, 4));
    } catch (err) {
      console.error(err);
      res.statusCode = 400;
      res.write("Unable to process request.");
    }
    res.end();
  })
  .listen(8080);

注意:代码中的选择器需要根据站点实际DOM结构调整,你可以用浏览器F12的元素选择器直接复制对应元素的选择器替换即可。

内容的提问来源于stack exchange,提问作者Anas Mutaleb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 00:09:01