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

Puppeteer遍历URL数组无法跳转页面问题求助

问题分析与解决方案

我帮你梳理下代码里的几个核心问题,这应该就是程序挂起的根源:

1. 页码起始值错误

你创建URL数组时循环从i=0开始,但观察你初始访问的URL是CurrentPage=1,说明网站的页码是从1开始计数的。CurrentPage=0的页面大概率不存在,或者加载后没有预期内容,直接导致后续等待逻辑超时。

2. page.goto后重复等待导航的冗余操作

page.goto()方法本身就会默认等待导航完成(waitUntil: load),你紧接着调用await page.waitForNavigation(),相当于在等待一个已经结束的导航事件,程序会陷入无限等待状态,直接挂起。

3. 单页应用的Hash导航特性

这个网站是单页应用(SPA),URL中#后的参数变化不会触发完整的页面刷新,page.goto()无法触发标准的导航事件,此时waitForNavigation完全不适用,应该改为等待页面关键元素加载来确认内容更新。


修复后的完整代码示例

const puppeteer = require("puppeteer-extra");
// 启用stealth插件规避反爬
puppeteer.use(require("puppeteer-extra-plugin-stealth")());

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  // 提取固定URL部分,只动态替换页码
  const baseUrl = "https://www.realtor.ca/map#ZoomLevel=14&Center=43.771556%2C-79.427630&LatitudeMax=43.78395&LongitudeMax=-79.38710&LatitudeMin=43.75916&LongitudeMin=-79.46816&view=list&Sort=6-D&PGeoIds=g10_dpz90869&GeoName=Willowdale%2C%20Toronto%2C%20ON&PropertyTypeGroupID=1&PropertySearchTypeId=1&TransactionTypeId=2&PriceMax=1500000&BuildingTypeId=1&Currency=CAD";
  
  // 初始化访问第一页
  await page.goto(`${baseUrl}&CurrentPage=1`, { waitUntil: "domcontentloaded" });
  await page.waitForSelector(".cardCon", { timeout: 15000 }); // 等待列表卡片加载完成

  // 提取第一页数据
  const firstPageData = await page.evaluate(() => {
    let data = [];
    let elements = document.querySelectorAll(".cardCon");
    for (var element of elements) {
      let price = element.children[0].children[1].childNodes[1].children[1].childNodes[1].childNodes[3].innerText;
      let address = element.children[0].children[1].childNodes[1].children[1].childNodes[1].childNodes[5].innerText;
      let details = element.children[0].children[1].childNodes[1].children[1].childNodes[3].innerText.replace(/\n/g, " ");
      let url = element.children[0].childNodes[3].href;
      data.push({ price, details, address, url });
    }
    return data;
  });
  console.log("第一页数据:", firstPageData);

  // 获取总页数
  let totalPageNumber = await page.$eval(
    "#ListViewPagination_Bottom > div > div > div > span.paginationTotalPagesNum",
    (element) => element.innerText
  );
  console.log("总页数:", totalPageNumber);
  let pageNumberToNumber = parseInt(totalPageNumber);

  // 创建URL数组:从1到总页数
  let urlList = [];
  for (let i = 1; i <= pageNumberToNumber; i++) {
    urlList.push(`${baseUrl}&CurrentPage=${i}`);
  }
  console.log("生成的页面URL列表:", urlList);
  console.log("待爬取页面数量:", urlList.length);

  // 遍历访问每一页(跳过第一页,因为已经爬过)
  for (let i = 1; i < urlList.length; i++) {
    const url = urlList[i];
    // 针对SPA的hash变化,导航后等待关键元素更新
    await page.goto(url, { waitUntil: "domcontentloaded" });
    await page.waitForSelector(".cardCon", { timeout: 15000 });
    
    // 提取当前页数据
    const currentPageData = await page.evaluate(() => {
      let data = [];
      let elements = document.querySelectorAll(".cardCon");
      for (var element of elements) {
        let price = element.children[0].children[1].childNodes[1].children[1].childNodes[1].childNodes[3].innerText;
        let address = element.children[0].children[1].childNodes[1].children[1].childNodes[1].childNodes[5].innerText;
        let details = element.children[0].children[1].childNodes[1].children[1].childNodes[3].innerText.replace(/\n/g, " ");
        let url = element.children[0].childNodes[3].href;
        data.push({ price, details, address, url });
      }
      return data;
    });
    console.log(`第${i+1}页数据:`, currentPageData);

    // 可选:添加随机延迟,避免被反爬拦截
    await page.waitFor(Math.random() * 2000 + 1000);
  }

  await browser.close();
})();

额外优化建议

  • 增加超时兜底:给所有等待元素的方法加上timeout参数,避免因页面加载异常导致程序挂起。
  • 添加异常捕获:用try/catch包裹导航和数据提取逻辑,单个页面出错不会中断整个爬取流程。
  • 复用数据提取逻辑:可以把提取页面数据的代码封装成函数,避免重复写相同的evaluate代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 10:57:42