Node.js脚本使用Mongoose保存MongoDB文档后挂起问题排查
问题诊断与修复方案
核心问题定位
脚本完成职位ID保存循环后挂起,无法执行后续代码,结合代码和现象,问题出在两处:
- 重复关闭浏览器触发隐性错误:
try块内已经执行了await browser.close(),但finally块又再次调用该方法,此时浏览器实例已销毁,会触发未被捕获的Promise拒绝,导致代码卡住。 - Mongoose连接配置不完整:
mongoose.connect未传入必要的连接选项,可能导致连接状态不稳定,残留的连接事件阻塞脚本退出。
修复步骤
1. 移除重复的浏览器关闭操作
删除finally块中的await browser.close(),因为try块内已经完成了浏览器的关闭:
finally { await mongoose.connection.close(); // 移除该行:await browser.close(); }
2. 完善Mongoose连接配置
为mongoose.connect添加标准连接选项,确保连接稳定建立与释放:
await mongoose.connect(`mongodb+srv:/xxxxxxxxx`, { useNewUrlParser: true, useUnifiedTopology: true });
3. 优化数据库查询效率(可选)
原循环中每次调用findOne查询效率较低,可改为批量查询已存在的ID,减少数据库请求次数:
// 替换原循环逻辑 const existingVacancyIds = (await Vacancy.find({ vacancyId: { $in: vacanciesIds } })).map(v => v.vacancyId); const newVacancies = vacanciesIds.filter(id => !existingVacancyIds.includes(id)).map(id => ({ vacancyId: id, active: true })); if (newVacancies.length > 0) { await Vacancy.insertMany(newVacancies); console.log(`批量保存了 ${newVacancies.length} 个新职位ID`); }
4. 添加全局错误捕获
在调用handler函数时添加错误捕获,避免未处理的Promise拒绝导致脚本静默挂起:
exports.handler().catch(err => { console.error("脚本执行出错:", err); process.exit(1); });
修复后的完整代码片段
const puppeteer = require("puppeteer"); const mongoose = require("mongoose"); require("dotenv").config(); const vacancySchema = new mongoose.Schema({ vacancyId: String, active: Boolean, }); const Vacancy = mongoose.model("Vacancy", vacancySchema); exports.handler = async () => { const browser = await puppeteer.launch({ headless: true, defaultViewport: null, }); const page = await browser.newPage(); try { await page.goto("https:xxxxxxxxx"); await page.waitForSelector("#vacancyTable"); await page.select("select#dt-length-0", "100"); let vacanciesIds = []; while (true) { let pageVacancies = await page.$$eval( "#vacancyTable tr > td.dt-type-numeric", (rows) => rows.map((row) => row.textContent.trim()) ); vacanciesIds = [...vacanciesIds, ...pageVacancies]; const nextButtonClassList = await page.$eval( "[data-dt-idx='next']", (el) => Array.from(el.classList) ); if (nextButtonClassList.includes("disabled")) break; const nextButton = await page.$("[data-dt-idx='next']"); await nextButton.click(); await page.waitForNetworkIdle(); } await browser.close(); await mongoose.connect(`mongodb+srv:/xxxxxxxxx`, { useNewUrlParser: true, useUnifiedTopology: true }); // 批量查询优化 const existingVacancyIds = (await Vacancy.find({ vacancyId: { $in: vacanciesIds } })).map(v => v.vacancyId); const newVacancies = vacanciesIds.filter(id => !existingVacancyIds.includes(id)).map(id => ({ vacancyId: id, active: true })); if (newVacancies.length > 0) { await Vacancy.insertMany(newVacancies); console.log(`批量保存了 ${newVacancies.length} 个新职位ID`); } console.log("Scrape check vs db finished"); } catch (error) { console.log(error.message); } finally { await mongoose.connection.close(); } }; // 添加全局错误捕获 exports.handler().catch(err => { console.error("脚本执行失败:", err); process.exit(1); });
内容的提问来源于stack exchange,提问作者Emiliano
相关产品推荐
相关产品推荐

