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

Pyppeteer+Flask开发截图程序遇Event loop is closed错误求助

解决Flask异步视图中Pyppeteer报错“RuntimeError: Event loop is closed”的问题

我写了一个处理HTTP请求、对查询参数里的链接截图的程序,想保持浏览器打开,通过新建标签页节省内存。但第一次请求能正常返回截图,第二次请求失败,报错RuntimeError: Event loop is closed。

代码示例

main.py

from flask import Flask, Response, request
from schedule_gen import capture_screenshot


app = Flask(__name__)

@app.route('/schedule', methods=['GET'])
async def schedule():
    # Processing query parameters.
    url = request.args.get('url')
    can_cache = request.args.get('canCache', False)

    if url is None:
        return "No url"

    screenshot = await capture_screenshot(url)
    return Response(screenshot, mimetype='image/png')



if __name__ == '__main__':
    app.run(debug=True, port=8800, host='0.0.0.0')

schedule_gen.py

from pyppeteer import launch

browser = None


async def get_browser():
    global browser
    if browser is None:
        browser = await launch(
            options={'args': ['--no-sandbox']},
            handleSIGINT=False,
            handleSIGTERM=False,
            handleSIGHUP=False
        )
    return browser


async def capture_screenshot(url):
    b = await get_browser()
    page = await b.newPage()

    # Set the viewport height to match the entire page
    await page.setViewport({'width': 1920, 'height': 0})

    await page.goto(url)

    screenshot = await page.screenshot({'fullPage': True})

    await page.close()
    return screenshot

问题原因

Flask默认的Werkzeug开发服务器不支持异步视图函数。第一次请求时,Flask会临时创建一个事件循环处理异步逻辑,但请求结束后这个循环会被直接关闭。第二次请求时,Pyppeteer的浏览器实例还绑定在已关闭的旧循环上,就触发了Event loop is closed的错误。

解决方案

方案一:改用ASGI服务器(推荐)

Flask 2.0+支持异步视图,但需要配合ASGI服务器运行,比如Uvicorn。

  1. 安装Uvicorn:
pip install uvicorn
  1. 修改main.py的启动代码:
from flask import Flask, Response, request
from schedule_gen import capture_screenshot

app = Flask(__name__)

@app.route('/schedule', methods=['GET'])
async def schedule():
    url = request.args.get('url')
    can_cache = request.args.get('canCache', False)

    if url is None:
        return "No url"

    screenshot = await capture_screenshot(url)
    return Response(screenshot, mimetype='image/png')

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host='0.0.0.0', port=8800, reload=True)

这样运行后,Uvicorn会维护一个持久的事件循环,Pyppeteer的浏览器实例可以一直复用,不会出现循环关闭的问题。

方案二:手动复用事件循环

如果不想换服务器,可以手动创建一个持久化的事件循环,把异步函数包装成同步调用:

修改main.py:

from flask import Flask, Response, request
from schedule_gen import capture_screenshot
import asyncio

app = Flask(__name__)
# 创建并设置全局事件循环
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

@app.route('/schedule', methods=['GET'])
def schedule():
    url = request.args.get('url')
    can_cache = request.args.get('canCache', False)

    if url is None:
        return "No url"

    # 用全局循环执行异步截图函数
    screenshot = loop.run_until_complete(capture_screenshot(url))
    return Response(screenshot, mimetype='image/png')

if __name__ == '__main__':
    app.run(debug=True, port=8800, host='0.0.0.0')

额外注意事项

  • 关闭Flask的debug自动重载(或者在reload时重新初始化浏览器),否则重载会导致浏览器实例失效。
  • 可以给浏览器添加退出钩子,确保应用关闭时正常关闭浏览器,避免资源泄漏:
    在schedule_gen.py末尾添加:
import atexit

async def close_browser():
    global browser
    if browser is not None:
        await browser.close()

# 用全局事件循环执行关闭操作
def shutdown():
    global loop
    loop.run_until_complete(close_browser())

atexit.register(shutdown)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 05:37:03