Flask + pyppeteer 实现截图

76 阅读1分钟

废话不多说直接扔代码

from flask import Flask, request, send_file
import os
import threading
import atexit
import asyncio
from pyppeteer import launch
from io import BytesIO
import time

from pyecharts import options as opts
from pyecharts.charts import Bar, Grid

# 创建 Flask 应用
app = Flask(__name__)

# 共享浏览器实例和事件循环
browser = None
event_loop = None

async def init_browser():
    global browser
    # 添加单进程模式和禁用内置退出处理
    browser = await launch(
        headless=False, # 修改为True,不显浏览器
        handleSIGINT=False,
        handleSIGTERM=False,
        handleSIGHUP=False,
        args=[
            '--no-sandbox',
            '--disable-setuid-sandbox',
            '--disable-dev-shm-usage',
            '--disable-accelerated-2d-canvas',
            '--disable-gpu',
            '--single-process'  # 添加单进程模式
        ]
    )

def run_event_loop(loop):
    asyncio.set_event_loop(loop)
    loop.run_forever()

def init_async_resources():
    global event_loop
    event_loop = asyncio.new_event_loop()
    t = threading.Thread(target=run_event_loop, args=(event_loop,), daemon=True)
    t.start()
    
    # 在事件循环线程中初始化浏览器
    future = asyncio.run_coroutine_threadsafe(init_browser(), event_loop)
    future.result()  # 等待浏览器初始化完成

@app.route('/screenshot/<name>')
def take_screenshot(name):
    url = request.args.get('url', 'https://cn.bing.com/search?q=' + name)
    
    # 将截图任务提交到事件循环线程
    future = asyncio.run_coroutine_threadsafe(async_screenshot(url), event_loop)
    image_data = future.result()
    
    return send_file(BytesIO(image_data), mimetype='image/png')

async def async_screenshot(html_path):
    # 为每个请求创建新的页面实例
    page = await browser.newPage()
    try:
        await page.setViewport({'width': 1920, 'height': 1080})
        await page.goto(url, {'waitUntil': 'networkidle2', 'timeout': 30000})
        screenshot = await page.screenshot({'fullPage': False}) # fullPage True全页
        return screenshot
    finally:
        await page.close()  # 确保关闭页面释放资源

@atexit.register
def shutdown():
    if browser:
        # 添加延迟关闭确保清理完成
        future = asyncio.run_coroutine_threadsafe(browser.close(), event_loop)
        try:
            future.result(5)  # 增加5秒超时
        except TimeoutError:
            pass
    if event_loop:
        # 等待事件循环完成剩余任务
        event_loop.call_soon_threadsafe(event_loop.stop)
        time.sleep(0.5)  # 添加短暂延迟

if __name__ == '__main__':
    init_async_resources()
    app.run(host='0.0.0.0', port=9000, threaded=True)