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

Playwright expose_function内使用Page实例方法失效问题

问题根因

expose_function 注册的回调默认运行在Playwright绑定的页面消息循环线程中,直接在回调内执行页面操作会触发两类问题:

  • 同步版本中,page.goto()、定位器调用这类API需要等待页面主线程返回响应,但此时页面主线程正阻塞在等待expose_function回调执行完成的状态,两边互相等待形成死锁,表现为调用无限期挂起。
  • 异步版本中,导航操作会直接销毁当前运行evaluate脚本的页面执行上下文,回调还未执行完成上下文就被回收,直接抛出执行上下文销毁的错误。
解决方案

不要在expose_function的回调逻辑里直接执行页面操作,回调只负责接收页面传来的参数,把实际的页面操作逻辑抛到独立的执行线程/异步任务中运行,让回调立刻返回打断死锁。

同步版本修复示例

通过后台线程承载实际页面操作逻辑,回调触发线程后立刻返回,不阻塞消息循环:

from playwright.sync_api import sync_playwright, Page
import threading

def search_title(page: Page, title: str):
    print(title)
    page.goto(f"https://bing.com?q={title}")

def main():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        context = browser.new_context(viewport={ 'width': 1920, 'height': 1024 })
        page = context.new_page()
        page.goto(f"https://wikipedia.org")
        
        def exposed_callback(title):
            # 独立线程跑页面操作,不阻塞当前回调
            threading.Thread(target=search_title, args=(page, title), daemon=True).start()
        context.expose_function("search_title", exposed_callback)
        
        page.evaluate("window.onload = () => search_title(document.title)")
        while True:
            page.wait_for_timeout(1000)

if __name__ == "__main__":
    main()

异步版本修复示例

通过asyncio.create_task把页面操作扔到独立异步任务中执行,回调触发任务后立刻返回,避免阻塞和上下文销毁报错:

from playwright.async_api import  Page, async_playwright
import asyncio

async def handle_title(page: Page, title: str):
    print(title)
    print(page)
    await page.goto(f"https://bing.com?q={title}")

async def main():
     async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context(viewport={ 'width': 1920, 'height': 1024 })
        page = await context.new_page()
        await page.goto(f"https://wikipedia.org")
        
        async def exposed_callback(title):
            # 独立任务跑页面操作,不阻塞当前回调
            asyncio.create_task(handle_title(page, title))
        await context.expose_function("handle_title", exposed_callback)
        
        await page.evaluate("window.onload = () => handle_title(document.title)")
        while True:
            await asyncio.sleep(1)

if __name__ == "__main__":
    asyncio.run(main())

注意:如果需要从页面侧获取回调执行结果,不要在页面脚本中直接await暴露函数的返回值做依赖导航的后续逻辑,导航会销毁当前页面上下文,所有运行在当前上下文的脚本都会被强制中断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 14:48:19