Puppeteer Node.js应用读取localStorage与Cookies时遭遇‘Execution context was destroyed’错误的排查求助
这个错误的核心原因很明确:当页面发生导航(包括重定向)时,旧页面的执行上下文会被销毁,而你在上下文销毁的时机尝试调用page.evaluate读取localStorage,自然会触发这个报错。咱们来一步步拆解你代码里的问题,然后给出修复方案:
你的代码存在的几个关键问题
错误的事件触发时机
你在Network.requestWillBeSent事件里执行page.evaluate和page.cookies——这个事件是在浏览器即将发送请求时触发的,此时旧页面已经开始卸载,执行上下文已经处于销毁过程中,根本无法安全地访问页面内的localStorage。冗余的
waitForNavigation调用page.goto()方法本身就会等待页面导航完成(默认waitUntil: load),之后再调用page.waitForNavigation()会导致代码等待一个不存在的后续导航,大概率会超时,还可能干扰正常的页面流程。未处理异步操作的并发问题
Network.requestWillBeSent事件是同步触发的,但你在里面嵌套了多层异步操作(async/await),这会导致多个异步任务在页面不稳定的状态下并行执行,进一步加剧上下文访问的风险。资源泄漏风险
代码里没有调用browser.close(),每次运行都会残留一个浏览器进程,时间长了会占用大量系统资源。
修复后的代码示例
下面是调整后的代码,解决了上述所有问题:
const dayjs = require('dayjs'); const AdvancedFormat = require('dayjs/plugin/advancedFormat'); dayjs.extend(AdvancedFormat); const puppeteer = require('puppeteer'); const { config } = require('./config'); const helpers = require('./helpers'); const logs = require('./logs'); const runEmulation = async (body) => { logs.debug('starting emulation'); // vars const argOptions = [], journey = []; // sandbox config if ((config.puppeteer.run_in_sandbox === 'true')) { argOptions.push('--no-sandbox'); } // initiate a Puppeteer instance with options and launch const browser = await puppeteer.launch({ args: argOptions, headless: (config.puppeteer.run_in_headless === 'true') ? true : false }); // launch a new page const page = await browser.newPage(); // 设置用户代理 await page.setUserAgent(body.userAgent); // 监听页面导航完成事件——此时新页面的执行上下文已经完全就绪 page.on('framenavigated', async (frame) => { // 只处理主框架的导航,忽略子框架 if (!frame.parentFrame()) { try { // 简化localStorage读取逻辑 const localStorageData = await page.evaluate(() => { return Object.entries(localStorage).map(([key, value]) => ({ key, value })); }); // 读取Cookies const cookies = await page.cookies(); journey.push({ url: frame.url(), type: 'Page Load', storage: { cookies: cookies ?? [], local: localStorageData ?? [] }, duration_in_ms: 0, duration_in_sec: 0, loaded_at: dayjs().valueOf() }); } catch (err) { logs.error(`Failed to scrape storage data: ${err.message}`); } } }); // 导航到目标URL,等待网络空闲确保页面完全加载 await page.goto(body.url, { waitUntil: 'networkidle2' }); console.log(journey); // 关闭浏览器,释放资源 await browser.close(); } exports.runEmulation = runEmulation;
关键修复点说明
- 改用
framenavigated事件:这个事件在页面导航完全完成后触发,新的执行上下文已经创建完成,此时访问localStorage和Cookies是绝对安全的。 - 过滤主框架:通过
!frame.parentFrame()确保我们只处理主页面的导航,避免子框架的无关操作干扰数据收集。 - 添加错误捕获:用
try/catch包裹存储读取操作,避免单个页面的读取失败导致整个程序崩溃。 - 优化localStorage读取:用
Object.entries替代手动循环,代码更简洁高效。 - 清理资源:添加
browser.close(),避免浏览器进程残留导致的资源泄漏。 - 明确导航等待策略:设置
waitUntil: 'networkidle2',确保页面加载完成后再继续后续逻辑,比默认的load更可靠。
内容的提问来源于stack exchange,提问作者Ryan H
相关产品推荐
相关产品推荐

