为何递归函数未陷入无限循环?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
相关产品推荐
相关产品推荐

