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

为何递归函数未陷入无限循环?Nightmare爬虫分页问题求助

嘿,我来帮你捋捋这个问题~

首先你遇到的“没设终止条件却没无限循环”其实是个假象——根本不是不会无限循环,而是你的递归压根没触发第二次!核心问题出在 Nightmare 的异步特性上,你没搞对它的执行逻辑。

为啥只打印1就结束?

Nightmare 所有操作(比如goto、evaluate)都是基于 Promise 的异步任务,如果你没在这些异步任务完成的回调里触发递归,而是直接同步调用,要么因为 Nightmare 实例被占用卡死,要么异步操作抛出了错误但你没捕获,导致整个流程直接静默终止了。举个例子,如果你的原代码是直接在函数里调用ab(page+1)而不是放在.then()里,那第一次的页面加载还没完成,递归的调用可能根本没被正确执行,或者直接报错中断了。

怎么修复才能实现分页爬取?

我给你两个可行的方案,都是围绕着等待异步操作完成+明确终止条件来做的:

方案1:用 Promise 链式调用(贴合你原来的递归思路)

const Nightmare = require('nightmare');
const nightmare = Nightmare({ show: true }); // 打开窗口能直观看到页面操作,方便调试

let currentPage = 1;
const baseUrl = '你的分页基础URL'; // 比如 'https://example.com/list?page='

function crawlPage(pageNum) {
  return nightmare
    .goto(`${baseUrl}${pageNum}`)
    .wait('body') // 别光等加载完成,最好指定下一页按钮或者页面核心元素,确保真的加载好了
    .evaluate(() => {
      // 这里既要拿标题,还要判断是不是最后一页
      const pageTitle = document.title;
      // 假设你的页面里,最后一页没有「下一页」按钮,用这个来判断
      const hasNextPage = document.querySelector('.next-page-btn') !== null;
      return { title: pageTitle, hasNext: hasNextPage };
    })
    .then(result => {
      console.log(`第${pageNum}页标题:${result.title}`);
      // 有下一页就继续递归,没有就结束并关闭Nightmare
      if (result.hasNext) {
        return crawlPage(pageNum + 1);
      } else {
        return nightmare.end();
      }
    })
    .catch(err => {
      // 一定要加错误捕获!不然出错了连提示都看不到
      console.error(`爬第${pageNum}页出问题了:`, err);
      return nightmare.end();
    });
}

// 启动爬取
crawlPage(currentPage);

方案2:用 async/await 写循环(更易读,避免递归嵌套)

如果你觉得递归看着绕,用async/await写个循环会更清晰:

const Nightmare = require('nightmare');
const nightmare = Nightmare({ show: true });

async function crawlAllPages() {
  let currentPage = 1;
  const baseUrl = '你的分页基础URL';

  while (true) {
    try {
      // 等待页面跳转完成
      await nightmare.goto(`${baseUrl}${currentPage}`);
      // 等待页面核心元素加载,确保能拿到数据
      await nightmare.wait('.page-content');
      
      // 获取当前页数据和是否有下一页
      const { title, hasNext } = await nightmare.evaluate(() => {
        return {
          title: document.title,
          hasNext: document.querySelector('.next-page-btn') !== null
        };
      });

      console.log(`第${currentPage}页标题:${title}`);

      // 没有下一页就跳出循环
      if (!hasNext) break;
      currentPage++;
    } catch (err) {
      console.error(`爬第${currentPage}页出错:`, err);
      break;
    }
  }

  // 爬完一定要关闭Nightmare实例,不然会留进程
  await nightmare.end();
}

// 启动
crawlAllPages();

几个关键提醒

  • 别忽略异步等待:所有 Nightmare 的操作都要等它完成(.then()或者await)再进行下一步,不然就是白忙活;
  • 必须加错误捕获:页面加载失败、元素找不到这些情况太常见了,不加捕获的话流程直接断了,你还不知道为啥;
  • 明确终止条件:不管用递归还是循环,都要靠页面上的标识(比如下一页按钮消失、页码达到最大值)来终止,不然真会无限循环;
  • 调试开show:true:把 Nightmare 的show设为true,能看到真实的浏览器窗口操作,哪里出问题一眼就能看出来。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:28:39