使用Node Crawler完成爬取无新链接时,如何触发结果输出代码?
解决爬虫完成后执行后续操作的问题
你的核心问题是当前代码用setTimeout延迟添加爬取任务,导致爬虫队列无法追踪所有待处理任务,drain事件会提前触发,没法在真正爬完所有链接后执行存文件等操作。另外async/await的使用没发挥作用,因为crawlAllUrls没有返回Promise。
下面是修改后的代码,解决这些问题并实现你要的功能:
const Crawler = require("crawler"); const fs = require("fs").promises; // 用Promise版fs方便异步写入 // 改用Set存储已爬链接,查找效率比数组高 const crawledUrls = new Set(); const baseUrl = 'http://www.five12.co.uk/'; // 初始化爬虫,配置爬取间隔(替代你原来的setTimeout) const c = new Crawler({ rateLimit: 5000, // 每5秒爬一个,和你原来的延迟一致 callback: handleCrawlResult }); function handleCrawlResult(err, res, done) { if (err) { console.error(`爬取${res.options.uri}失败:`, err); return done(); } const $ = res.$; try { $("a").each((_, element) => { let href = $(element).attr('href'); if (!href) return; // 解析相对路径,转换成完整URL const fullUrl = new URL(href, baseUrl).href; // 只处理当前域名的内部链接,且未爬过的 if (fullUrl.startsWith(baseUrl) && !crawledUrls.has(fullUrl)) { crawledUrls.add(fullUrl); console.log(`发现新链接: ${fullUrl}`); // 直接加入爬虫队列,由rateLimit控制速度 c.queue(fullUrl); } }); } catch (e) { console.error(`处理${res.options.uri}时出错:`, e); } done(); } // 所有任务完成时触发drain事件,在这里执行后续操作 c.on('drain', async function() { console.log("\n爬取完成!所有内部链接如下:"); const allUrls = Array.from(crawledUrls); allUrls.forEach(url => console.log(url)); // 写入文本文件 try { await fs.writeFile('爬取结果.txt', allUrls.join('\n')); console.log("结果已写入爬取结果.txt"); } catch (writeErr) { console.error("写入文件失败:", writeErr); } // 如果要写入Google表格,可以用对应的npm包,思路是: // 1. 安装对应包 // 2. 配置Google API权限 // 3. 在drain事件中调用API将allUrls写入表格 }); // 启动爬虫 c.queue(baseUrl);
关键修改说明:
- 用Set存储已爬链接:
Set的has方法时间复杂度是O(1),比数组includes的O(n)高效得多,爬大量链接时差距明显。 - 用爬虫自带的rateLimit:直接在初始化爬虫时配置
rateLimit,不用自己写setTimeout,爬虫会自动控制任务间隔,且所有任务都在队列管理中,drain事件会在所有任务完成后正确触发。 - 正确解析URL:用
URL类处理相对路径,避免手动拼接出错,比如处理/page这类相对路径时,会自动转换成完整的http://www.five12.co.uk/page。 - 异步写入文件:用
fs.promises的writeFile,配合async/await处理文件写入的异步操作,避免回调嵌套。
内容的提问来源于stack exchange,提问作者Dale Moir
相关产品推荐
相关产品推荐

