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

Pyppeteer与Quart结合出现Event loop is closed错误求助

解决Quart + Pyppeteer中"Event loop is closed"的清理问题

你的问题根源在于用同步的atexit钩子去处理异步清理操作,而Quart作为异步Web框架,在停止时会先关闭事件循环,之后atexit的同步函数再去调用run_until_complete就会触发"Event loop is closed"错误。另外,全局直接用run_until_complete启动浏览器也不符合Quart的异步生命周期规范。

正确的做法是利用Quart提供的异步生命周期钩子,在框架启动前初始化浏览器,在框架停止后(事件循环仍运行时)清理浏览器实例,完全贴合异步环境的执行逻辑。

修正后的代码示例

#!/usr/bin/env python3
import pyppeteer
from quart import Quart, Response, request

app = Quart(__name__)
browser = None  # 先初始化空变量,后续在钩子中赋值

# 在Quart启动服务前初始化浏览器
@app.before_serving
async def startup():
    global browser
    browser = await pyppeteer.launch()

# 在Quart停止服务后清理浏览器
@app.after_serving
async def cleanup():
    await browser.quit()

# 示例路由,使用浏览器实例
@app.route('/')
async def index():
    page = await browser.newPage()
    await page.goto('https://example.com')
    content = await page.content()
    await page.close()
    return Response(content, mimetype='text/html')

if __name__ == '__main__':
    app.run(host='0.0.0.0')

为什么这个方案可行?

  • @app.before_serving:这个钩子会在Quart的事件循环启动后、开始处理请求前执行异步代码,确保浏览器在正确的异步环境中初始化,不需要手动调用run_until_complete。
  • @app.after_serving:这个钩子会在Quart停止接收请求后、事件循环关闭前执行异步清理操作,此时事件循环仍处于运行状态,await browser.quit()可以正常执行,不会出现"Event loop is closed"的错误。
  • 完全避免了同步/异步代码的冲突,贴合Quart的异步设计理念。

额外优化建议

如果需要全局管理浏览器实例的并发访问,可以添加一个异步锁(asyncio.Lock)来避免多个请求同时操作浏览器导致的问题:

import asyncio

# 在全局添加异步锁
browser_lock = asyncio.Lock()

@app.route('/')
async def index():
    async with browser_lock:  # 确保同一时间只有一个请求操作浏览器
        page = await browser.newPage()
        await page.goto('https://example.com')
        content = await page.content()
        await page.close()
    return Response(content, mimetype='text/html')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:16:50