
Streamlit 应用中直接执行阻塞式网络请求(如 requests.get)会导致界面长时间无响应,因其主线程被阻塞;正确做法是将 API 调用与用户交互绑定(如按钮触发),并配合 st.spinner 提供反馈,避免页面冻结。
streamlit 应用中直接执行阻塞式网络请求(如 requests.get)会导致界面长时间无响应,因其主线程被阻塞;正确做法是将 api 调用与用户交互绑定(如按钮触发),并配合 st.spinner 提供反馈,避免页面冻结。
在 Streamlit 中,所有顶层代码(即未包裹在函数或交互逻辑中的代码)会在每次脚本重运行时立即执行——包括 requests.get()。当你写 streamlit run app.py 时,Streamlit 会持续监听源码变化并自动重载,而若你在脚本开头就发起一个未加控制的网络请求,它将在每次重载(甚至热重载、小修改保存后)时重复执行。更关键的是:该请求会阻塞 Streamlit 的主线程,导致整个 UI 冻结、无响应、无日志输出,看似“卡住几分钟”,实则是同步请求超时(默认约 30–60 秒)或网络中间件(如代理、防火墙、DNS 缓慢)叠加造成的假性长延迟。
而 python app.py 可秒完成,是因为它只是普通 Python 脚本执行,无 UI 框架调度开销,也无重载机制干扰。
✅ 正确实践:将请求封装为函数,并仅在用户显式操作(如点击按钮)时调用:
import streamlit as st
import requests
API_URL = "https://jsonplaceholder.typicode.com/posts"
def fetch_posts():
try:
with st.spinner("正在获取文章列表..."):
response = requests.get(API_URL, timeout=10) # 建议始终设置 timeout
if response.status_code == 200:
return response.json()
else:
st.error(f"API 请求失败:{response.status_code}")
return None
except requests.exceptions.Timeout:
st.error("请求超时,请检查网络连接。")
return None
except requests.exceptions.RequestException as e:
st.error(f"网络异常:{e}")
return None
st.title("? Streamlit API 数据获取示例")
st.caption("点击按钮触发安全、可中断的 HTTP 请求")
if st.button("加载文章数据", type="primary", use_container_width=True):
data = fetch_posts()
if data and len(data) > 0:
st.success(f"✅ 成功获取 {len(data)} 篇文章!")
# 展示前 3 条以提升渲染性能
st.json(data[:3])
st.info("完整数据已加载,如需进一步处理,请在代码中操作 `data` 变量。")
? 关键要点:
-
永不将
requests调用写在函数外部或if __name__ == "__main__":之外的顶层作用域; - 使用
st.button/st.selectbox等 widget 触发请求,确保按需执行; - 总是添加
timeout参数(推荐 5–15 秒),防止无限等待; - 用
try/except捕获常见网络异常(Timeout,ConnectionError,RequestException); - 利用
st.spinner、st.success、st.error提供实时状态反馈,提升用户体验; - 对大数据响应,避免直接
st.json(data)全量渲染——可截取、分页或转为st.dataframe()。
? 进阶提示:对于高频或需缓存的请求,可结合 @st.cache_data(ttl=300)(Streamlit ≥ 1.24)实现自动缓存,避免重复调用:
@st.cache_data(ttl=300) # 缓存 5 分钟
def cached_fetch_posts():
return requests.get(API_URL, timeout=10).json()
遵循以上模式,你的 Streamlit 应用即可稳定、快速、友好地集成外部 API。











