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

Telegram Bot使用Playwright遇greenlet.error,如何全局声明Browser?

问题解决:Playwright greenlet.error 与全局Browser复用

错误原因

你遇到的greenlet.error: cannot switch to a different thread是因为:

  • Playwright的同步API(sync_playwright)与创建它的线程绑定,无法跨线程调用
  • 你给CommandHandler设置了run_async=True,导致start函数会在python-telegram-bot的线程池里执行,和全局Browser所在的主线程不是同一个,触发了跨线程调用的错误

可以全局复用Browser,两种方案任选:

方案一:改用异步API(推荐)

python-telegram-bot从v13开始支持异步处理,Playwright也有异步API,两者结合可以完美解决线程问题,同时全局复用Browser,避免每次启动的3秒延迟。

修改后的代码:

from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
from playwright.async_api import async_playwright

args = [
    "--no-sandbox",
    "--disable-setuid-sandbox",
    "--use-gl=egl",
]

# 全局维护Browser实例
browser = None

async def init_playwright():
    global browser
    playwright = await async_playwright().start()
    browser = await playwright.chromium.launch(headless=True, args=args)

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    global browser
    # 从全局Browser创建新页面
    page = await browser.new_page()
    await page.goto("https://google.com")
    await page.screenshot(path="example.png")
    await page.close()
    await context.bot.send_photo(chat_id=update.effective_chat.id, photo=open("example.png", "rb"))

async def main():
    # 先初始化Playwright和Browser
    await init_playwright()
    # 创建Telegram应用
    app = ApplicationBuilder().token('你的TOKEN').build()
    app.add_handler(CommandHandler('start', start))
    # 启动轮询
    await app.run_polling()

if __name__ == '__main__':
    import asyncio
    asyncio.run(main())

方案二:线程本地存储隔离(同步API兼容)

如果坚持用同步API和旧版Updater,可以用threading.local给每个线程单独分配Browser实例,避免跨线程调用,同时每个线程复用自己的Browser。

修改后的代码:

from telegram.ext import Updater, CommandHandler
from playwright.sync_api import sync_playwright
import threading

args = [
        "--no-sandbox",
        "--disable-setuid-sandbox",
        "--use-gl=egl",
    ]

# 线程本地存储,每个线程有独立的Playwright和Browser
thread_local = threading.local()

def get_thread_browser():
    # 线程第一次调用时初始化Browser
    if not hasattr(thread_local, 'browser'):
        playwright = sync_playwright().start()
        thread_local.browser = playwright.chromium.launch(headless=True, args=args)
        thread_local.playwright = playwright
    return thread_local.browser

def start(update, context):
    # 获取当前线程的Browser实例
    browser = get_thread_browser()
    page = browser.new_page()
    page.goto("https://google.com")
    page.screenshot(path="example.png")
    page.close()
    context.bot.send_photo(chat_id=update.effective_chat.id, photo=open("example.png", "rb"))  

# 注意:workers数量不要过大,每个worker线程会创建一个Browser,占用资源会增加
updater = Updater('你的TOKEN', use_context=True, workers=4)
updater.dispatcher.add_handler(CommandHandler('start', start, run_async=True))
updater.start_polling()
updater.idle()

注意事项

  • 方案一中,建议升级到python-telegram-bot v14+,Application是官方推荐的新写法,比Updater更灵活
  • 方案二中,workers参数不要设得太大(比如你原来的124),每个worker对应一个Browser实例,会占用大量内存和CPU资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 09:47:07