Streamlit多按钮共用Playwright Page对象及截图按钮阻塞问题的解决方法
Streamlit多按钮共用Playwright Page对象及截图按钮阻塞问题的解决方法
嗨,我来帮你搞定这个Streamlit和Playwright集成的阻塞问题!
你遇到的截图按钮阻塞问题,核心原因是每次点击Streamlit按钮时,脚本会重新运行,而你用asyncio.run()单独调用每个异步函数,会创建新的事件循环,和之前打开页面时的事件循环冲突——之前的page对象属于旧的事件循环,在新循环里调用它的异步方法就会卡住。另外,之前的代码还存在Playwright实例未正确关闭的资源泄漏问题。
下面是修正后的完整代码,我会一步步给你解释修改的关键要点:
import streamlit as st from playwright.async_api import async_playwright import asyncio import nest_asyncio # 应用nest_asyncio以支持嵌套事件循环 nest_asyncio.apply() # 针对Windows环境设置ProactorEventLoop,解决Playwright异步兼容问题 if st.runtime.exists(): asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) # 初始化session_state,保存跨交互的核心对象 if "browser" not in st.session_state: st.session_state.browser = None if "page" not in st.session_state: st.session_state.page = None if "playwright" not in st.session_state: st.session_state.playwright = None async def open_browser(): # 避免重复打开浏览器,防止资源浪费 if st.session_state.browser is not None: st.warning("浏览器已经处于打开状态啦") return # 启动Playwright并打开Chromium浏览器 st.session_state.playwright = await async_playwright().start() st.session_state.browser = await st.session_state.playwright.chromium.launch(headless=False) st.session_state.page = await st.session_state.browser.new_page() await st.session_state.page.goto("https://www.baidu.com") st.success("网页已成功打开!") async def take_screenshot(): if st.session_state.page: await st.session_state.page.screenshot(path="screenshot.png") st.success("截图已保存为screenshot.png") else: st.error("请先打开网页再进行截图操作哦") async def close_browser(): if st.session_state.browser: # 正确关闭浏览器并停止Playwright实例,避免资源泄漏 await st.session_state.browser.close() await st.session_state.playwright.stop() # 重置session_state中的状态,为下一次操作做准备 st.session_state.browser = None st.session_state.page = None st.session_state.playwright = None st.success("浏览器已关闭") else: st.error("浏览器还没打开呢") # Streamlit UI界面 st.title("Playwright 与 Streamlit 集成演示") # 打开网页按钮 if st.button("打开网页"): loop = asyncio.get_event_loop() loop.run_until_complete(open_browser()) # 生成截图按钮 if st.button("生成截图"): loop = asyncio.get_event_loop() loop.run_until_complete(take_screenshot()) # 关闭网页按钮 if st.button("关闭网页"): loop = asyncio.get_event_loop() loop.run_until_complete(close_browser())
关键修改点说明:
统一事件循环执行异步操作:
把原来的asyncio.run()替换成asyncio.get_event_loop().run_until_complete(),这样所有异步操作都会复用我们预先配置好的事件循环(已经应用了nest_asyncio和Windows专属的ProactorEventLoopPolicy),避免了循环冲突导致的阻塞。新增Playwright实例到Session State:
之前的Playwright是局部变量,关闭浏览器后无法停止这个实例,会造成资源泄漏。现在把它存入st.session_state,关闭浏览器时可以正确调用playwright.stop()清理资源。添加重复打开判断:
在open_browser函数里增加了浏览器状态判断,避免用户多次点击打开按钮创建多个浏览器实例。优化提示信息:
把提示改成更贴近中文用户的表达,让操作反馈更清晰。
额外注意事项:
- 确保你已经安装了所有依赖:
pip install streamlit playwright nest-asyncio playwright install chromium - 运行脚本时直接用
streamlit run 你的脚本名.py,不要用普通Python运行,因为Streamlit需要在它的运行时环境中执行。
备注:内容来源于stack exchange,提问作者ffsxsy
相关产品推荐
相关产品推荐

