☰
Streamlit 实战:看板、表单与模型部署的网站集合
2026/10/20 11:46:56 网站建设 项目流程

简介:这是一套基于Streamlit制作的Web应用示例合集,面向Python开发者、数据分析师和数据可视化爱好者,帮助大家通过现成代码快速上手Streamlit,无需深入学习HTML、CSS等前端技术,就能搭建出交互式数据页面,适合快速验证想法、搭建内部工具或学习数据应用开发流程。整包包含7个文件,总大小518KB,其中有两个Python脚本负责核心功能逻辑,三张JPG图片作为界面展示素材,另配一份说明文档与依赖清单,列出项目结构、运行方式及所需库列表,便于直接运行学习和二次修改。目前已有两千七百二十二人学习这份示例集。示例覆盖调色板生成、文件夹浏览、图片展示等典型场景,并体现了页面布局、交互控件、数据加载与显示等常见写法,借助说明文档可快速完成环境配置,观察Streamlit的“代码即界面”核心思想,之后替换数据或组件,就能改写出自己的数据看板、工具站点或原型,对入门和日常工作参考都很有价值。

1. 网站集合的价值:Streamlit 让你用纯 Python 兑现交互页面

Streamlit 是一个把 Python 脚本直接变成网页的库,而这个项目,是一套用 Streamlit 写好的网站集合:数据看板、表单录入、模型预测这些最常见场景都包含在内。真实情况是,分析师手里的 csv 和模型,离“能被同事点两下”的网页,就差一个前端距离,学 HTML、CSS、JS 成本太高,Streamlit 把这条路压缩到只写 Python。这套集合的价值,是把侧边栏筛选、表格排序、按钮触发、结果回显这些反复出现的交互套路,做成可改的模板。适合赶交付的后端开发者、刚接触 Web 的数据岗,以及需要在团队内部快速搭工具的人。改改数据源和字段名,就能让页面跑起来。

2. 集合里装了什么:看板、表单、模型 Demo 的三种基本形态

这个集合不是一个大而全的应用,而是按交互形态拆成一堆独立页面。理解它最有效的方式,是把它按“信息流向”分成三类:数据从文件流向图表的是看板,数据从用户流向存储的是表单,数据从模型流向结果是预测 Demo。每一类在 Streamlit 里的写法差异很大,混着用就会出问题,单独拆开反而能复用。

2.1 看板类:st.bar_chart 与 st.dataframe 的组合套路

集合里数量最多的就是看板类页面,整体骨架都是“读数据 → 筛选 → 画图 → 展示表格”。一个最简实现如下:

import pandas as pd import streamlit as st @st.cache_data def load_data(): # 集合里的示例数据是 csv,路径建议用相对路径 return pd.read_csv("data/sales.csv") df = load_data() st.title("销售看板") st.bar_chart(df.groupby("region")["amount"].sum()) st.dataframe(df.head(200), use_container_width=True)

逻辑说明:st.bar_chart 直接接收 DataFrame 或 Series,省去手动配置 matplotlib 的坐标轴和配色;st.dataframe 渲染的是带排序、缩放和滚动条的表格,文件比较大的时候能按需加载,不会一上来就卡死页面。参数说明:use_container_width=True 让表格撑满外层容器,避免缩在左侧窄窄一列;head(200) 限制展示条数,作用是防止大文件把前端拖垮。

这个组合是看板页的基本盘。集合里有些页面会升级成 st.plotly_chart,效果更接近商业报表,带 tooltip、缩放、框选,但依赖体积大、首次加载也慢。我的习惯是:只展示趋势用 st.bar_chart 和 st.line_chart,需要拖拽和悬停看明细再换 plotly。替换时注意 plotly 接收的是 figure 对象,而不是 DataFrame,直接喂数据会报错。

2.2 表单交互类:st.form 与 st.session_state 的状态管理

表单类页面承担“录入 → 提交 → 回显”的角色,典型场景是资产登记、参数配置。最容易犯的错是把输入框直接散落在页面顶层,这样用户每次点一下控件,脚本就从第一行重跑,前面填的内容全部重置。集合里的正确写法是把控件包进 st.form:

import streamlit as st if "records" not in st.session_state: st.session_state.records = [] with st.form("asset_form"): name = st.text_input("资产名称") owner = st.selectbox("负责人", ["A组", "B组", "C组"]) submitted = st.form_submit_button("登记") if submitted: st.session_state.records.append({"name": name, "owner": owner}) st.success("已登记,当前共 %d 条" % len(st.session_state.records))

逻辑说明:st.form 内部的所有控件在用户操作时不会立刻触发重跑,只有按下 form_submit_button,表单里所有值才一次性返回,这保证了填写过程中其他控件保持原样。st.session_state 用来保存跨重跑的记录,因为普通 Python 变量在每次脚本重跑后都会重新初始化,只有写进 session_state 才能留住。参数说明:with st.form("asset_form") 的字符串是表单唯一标识,同一个页面上多个表单不能重名;form_submit_button 可以加 type="primary" 让按钮高亮,用 use_container_width 控制宽度。

集合里的表单页还会和 st.data_editor 配合,比如让用户直接在一个可编辑表格里改数据,提交后统一覆盖原 DataFrame。st.data_editor 返回的是表格当前值,配合 session_state 存档,对内部工具来说比一个个输入框更高效。

2.3 模型部署类:加载模型与预测回显

模型部署类页面负责把训练好的模型接上页面:左侧给特征,右侧出结果。这一类页面真正的难点不是模型本身,而是模型文件路径怎么管理、结果怎么格式化才不会让用户误解。一个常见骨架:

import joblib import streamlit as st model_path = st.sidebar.text_input("模型目录", "model/reg.pkl") model = joblib.load(model_path) x1 = st.sidebar.slider("特征A", 0, 100, 50) x2 = st.sidebar.number_input("特征B", value=1.0) if st.sidebar.button("开始预测"): pred = model.predict([[x1, x2]])[0] st.metric("预测结果", round(float(pred), 2))

逻辑说明:joblib.load 把训练好的模型读进内存;slider 和 number_input 分别对应离散和连续特征的输入;st.metric 把结果渲染成带标签的指标卡,比直接写 st.write 更像产品。参数说明:model_path 用 text_input 的好处是不改代码就能切换模型文件,但要注意这里的路径是相对“启动目录”而不是“脚本目录”,这个差别是路径踩坑的第一来源。预测结果 round 成两位小数,避免一长串浮点把用户看懵。

集合里更完整的 Demo 还会加上 st.file_uploader,让用户上传 csv,pandas 读取后批量预测,再用 st.download_button 把结果下载。这套流程对不想碰命令行的同事很友好,但大文件上传速度慢,我一般提醒超过几十 MB 还是先放到服务器本地路径再加载。

页面类型核心组件适用场景
看板类st.bar_chart / st.dataframe报表展示、趋势分析
表单类st.form / st.session_state登记录入、参数配置
模型类joblib + st.metric推理预测、结果回显

三类页面覆盖了内部工具八成的需求。想快速上手这套集合的话,不用一开始读全部代码,直接挑一个单文件运行,改改数据路径就能看到效果,比从头读一百行再动手更省时间。

3. 把集合跑起来:环境搭建、启动命令与 config.toml 配置

拿到集合包第一步不是打开编辑器,而是先把环境搭干净再谈运行。Streamlit 对 Python 版本不算挑剔,但依赖库之间偶尔会有版本冲突,用虚拟环境隔离能省掉一大半玄学问题。配置层面则有三件事要做:创建虚拟环境、用正确方式启动、把主题和端口写进配置文件。

3.1 虚拟环境与依赖安装顺序

先把项目拆到本地,然后进目录建虚拟环境。我一般按这个顺序执行:

cd streamlit-project python -m venv venv source venv/bin/activate pip install -r requirements.txt

Windows 下把 source venv/bin/activate 换成 venv\Scripts\activate。两个容易被忽略的点:一是必须先激活再 pip install,否则包装进全局环境,之后换项目会互相污染;二是 requirements.txt 里有 pandas、numpy 这类带编译的包时,直接用 pip 的默认 wheel,不要自己去源码编译。环境一旦弄脏,最干脆的办法是把 venv 目录整个删掉重建,而不是手动装装拆拆,那才真的浪费时间。

requirements.txt 里通常会把 streamlit 写成固定版本,比如 streamlit==1.x.x。如果你看到没锁版本,强烈建议自己补上。Streamlit 的 API 在版本升级时偶尔会改,比如部分图表函数的参数变化,集合里的代码按某个旧版写好后,在新环境跑可能出现 warning 甚至直接报错。锁定版本等于给项目留了后悔药。

3.2 启动的三种方式:单文件、多页面与外部访问

集合里不同用途的页面有不同启动方式。单文件启动是最常用的:

streamlit run app_dashboard.py

启动后终端会显示本地地址,浏览器自动打开。如果集合用了多页面结构,Streamlit 会把 pages/ 目录下的所有 py 文件自动识别为子页面,启动时只需指定主入口:

streamlit run 01首页.py

这时左侧会出现页面列表,点一下就能切换。第三种是允许局域网访问,加上 --server.address:

streamlit run app_dashboard.py --server.address=0.0.0.0 --server.port=8501

参数说明:--server.address=0.0.0.0 表示监听所有网卡,同一局域网里的机器能用服务器 IP 访问;默认只绑定 localhost,外部机器连不进来。--server.port 指定监听端口,默认 8501,多个项目并发跑就要手动换。需要特别提醒:这种裸启动方式没有任何鉴权,内部用可以,对外提供服务一定要放在可控内网,或者前面套一层反向代理统一控制访问,否则相当于把页面和数据直接暴露出去。

3.3 config.toml:主题、端口与 CORS 配置

除了命令行,Streamlit 还支持配置文件,位置固定在项目的 .streamlit/config.toml 下。集合里最常见的配置:

[theme] primaryColor = "#4F8BF9" [server] port = 8502 runOnSave = true [browser] gatherUsageStats = false

配置改完要重启进程才生效。theme 下的 primaryColor 会改变所有按钮和选中状态的颜色,适合把页面统一成公司品牌色;server.port 的作用和 --server.port 一样,两边同时存在时命令行优先;runOnSave=true 会在编辑器保存文件后自动刷新浏览器,本地开发很舒服,但部署到正式环境最好关掉,避免代码保存时页面被反复重载。

还有一个需要注意的坑:把页面放到容器或反向代理后面时,浏览器访问路径和服务器监听路径可能不一致,需要通过启动命令或配置正确设置路由前缀。集合本身不包含部署环境的配置,因为内部工具通常直接跑在内网服务器上,难度不大。真正对外发布时,我会让反向代理把特定访问路径转发到本地 8501 端口,不把 Streamlit 进程直接暴露在公网地址上。

4. 拆一个完整案例:从 CSV 到可筛选的交互看板

为了讲清这套集合的实际用法,我挑一个销售看板 Demo 从头拆一遍。这个页面基本覆盖了集合里看板类的所有关键技巧:缓存、筛选联动、指标卡、导出。把这一套吃透,看板类页面基本就通了。

4.1 数据加载与缓存:st.cache_data 的两种写法

集合里的看板页面几乎都有一个加载数据的函数,用 @st.cache_data 装饰器包起来:

import pandas as pd import streamlit as st @st.cache_data(ttl=600) def load_orders(path="data/orders.csv"): df = pd.read_csv(path) df["order_date"] = pd.to_datetime(df["order_date"]) return df

逻辑说明:Streamlit 页面每次交互都会从第一行执行到最后一行,如果不加缓存,用户点一下侧边栏筛选,整个 csv 就重新读一遍,数据超过几万行就会明显卡顿。st.cache_data 会根据函数参数和函数体内容生成缓存键,参数没变时直接返回缓存结果,ttl=600 表示缓存有效期 10 分钟,到期后重新计算。参数说明:ttl 适合数据定期更新的场景,比如每天更新一次就设成 3600 或 86400;如果文件基本只增不改,可以不传 ttl,让缓存永久生效。数据文件被手动替换后,旧缓存还会继续命中,这时需要主动清理缓存,这也是后面避坑章里要展开的内容。

4.2 侧边栏筛选与图表联动:widget 的典型闭环

筛选是看板类页面最核心的交互。Streamlit 的写法是:读取 widget 的值 → 过滤 DataFrame → 把过滤结果喂给图表:

regions = st.sidebar.multiselect( "选择区域", df["region"].unique(), default=list(df["region"].unique()[:2]) ) filtered = df[df["region"].isin(regions)] daily = filtered.groupby("order_date")["amount"].sum() st.line_chart(daily) st.write("筛选后共 %d 条记录" % len(filtered))

逻辑说明:st.sidebar.multiselect 的第二个参数是候选选项集,第三个参数是默认选中值。不传 default 时,页面首次加载会返回空列表,图表跟着空白,新手常在这里翻车。filtered 这步是普通的 pandas 布尔索引,Streamlit 本身不做任何数据过滤,所有“动态效果”都来自脚本重跑后重新计算。参数说明:multiselect 的 key 参数很重要,如果多个页面都用了 "region" 这个名字,切换页面时选中状态会被带到另一个页面。集合里的多页面 Demo 专门踩过这个坑,解决办法是给 key 加上页面前缀,比如 dashboard_region 和 analysis_region。

4.3 指标卡与导出:st.metric、下载按钮的配合

看板不能只有图表,还需要总结性数字和导出能力。典型写法:

total_amount = filtered["amount"].sum() col1, col2, col3 = st.columns(3) col1.metric("总金额", f"{total_amount:,.0f}") col2.metric("订单数", len(filtered)) col3.metric("平均单笔", f"{total_amount / max(len(filtered), 1):,.2f}") csv_data = filtered.to_csv(index=False).encode("utf-8-sig") st.download_button( "导出筛选结果", data=csv_data, file_name="filtered_orders.csv", mime="text/csv" )

逻辑说明:st.columns 创建并排布局,col1.metric 把带格式的字符串渲染成指标卡,适合放在看板顶部让用户一眼读到核心数据。st.download_button 把 csv 字符串转成 bytes 直接下载,不需要写后端接口,文件只存在内存里,比临时生成文件再清理干净得多。参数说明:encode("utf-8-sig") 是给中文用的,不加这个 BOM,Excel 直接打开 csv 会乱码;file_name 支持带日期,比如 filtered_orders_20260415.csv,方便按批次存档。f"{total_amount:,.0f}" 是千分位格式化,一个小细节,但直接影响页面的专业观感。

拆完这个案例就能看到,集合里的看板页面从结构上就是这一套东西:加载缓存、侧边栏筛选、指标卡和导出。换数据源、改 groupby 维度,就能覆盖绝大多数内部看板需求。真正决定页面能不能让同事愿意天天打开,往往不是图表多炫,而是筛选顺不顺、导出数据准不准。

5. 避坑:Streamlit 运行时的五个常见问题与排查

这套集合本身代码不算复杂,但运行环境的差异会制造出各种“在我电脑上是好的,到你那边就翻车”的状况。下面这五条是我在拆这类项目时真实踩过的坑,按现象、原因、解决的思路整理出来,基本覆盖了 Streamlit 内部工具最常见的故障点。

5.1 缓存不刷新:文件改了,页面数据还是旧数据

现象:csv 文件明明更新了,页面上显示的却还是前天甚至上周的数据。反复刷新浏览器也没有变化,看起来像“黑匣子”。

原因:st.cache_data 默认把函数参数和函数体代码当作缓存 key,它不感知外部文件内容的修改。只要文件路径没变、参数没变,缓存就一直命中,旧数据就被反复拉出来展示。

解决:在调试入口加一个手动清缓存按钮,代码是这样:

if st.sidebar.button("清缓存并刷新"): st.cache_data.clear() st.rerun()

st.cache_data.clear() 会清掉该页面所有缓存数据,st.rerun() 强制脚本重跑。对内部工具来说,加这样一个入口比教用户重启服务更省事。

5.2 按钮状态一触就丢:页面上的勾选和输入全部重置

现象:页面放了几个筛选框和一个“查询”按钮,用户填好条件后点击按钮,页面上所有输入框立刻恢复默认值,填的内容全部消失。

原因:Streamlit 的脚本是单向渲染,widget 的值默认随每次重跑重置,只有通过 key 注册到 session_state 里的值才会保留。普通 Python 变量无法跨重跑存活。

解决:每个需要保留状态的 widget 都显式指定 key,例如 st.text_input("项目名", key="project_name")。值会自动写入 session_state 并在重跑后恢复。需要跨页面保存的数据,提前初始化到 session_state,再用 session_state["project_name"] 读取。st.form 的提交机制能解决一部分问题,但前提是控件必须包在 form 里面,form 外散落的控件依然会每次参与重跑。

5.3 中文乱码:页面正常,导出表格却乱码

现象:页面上显示的中文一切正常,但用 st.download_button 导出的 csv 文件用 Excel 打开后乱码,或者 plotly 图表的坐标轴中文变成方块。

原因:页面本身走 UTF-8 渲染,所以浏览器里看不出问题。导出时如果直接 df.to_csv().encode("utf-8"),Excel 默认按本地编码解析,就读出了乱码。图表乱码则是因为绘图库默认字体集里没有中文字体,渲染时找不到对应字形。

解决:导出统一 encode("utf-8-sig"),加上 BOM 头,Excel 就能正确识别。图表中文字体需要在绘图时显式指定包含中文的字体,比如 SimHei 或 Microsoft YaHei,并保证运行环境里装了对应字体。Linux 服务器上常见的坑是系统字体目录里没有中文字体,即使代码写了字体内核也渲染不出来。

5.4 端口被占:第二个项目起不来

现象:跑完一个页面后再启动集合里的另一个页面,终端直接报 8501 already in use,页面一直显示请求超时。

原因:Streamlit 默认所有实例都绑定 8501 端口,同一台机器上多个项目并发跑,后启动的进程就抢不到端口。

解决:启动时显式指定端口:streamlit run app_analysis.py --server.port=8510。更稳妥的做法是写一个启动脚本,把项目和端口映射关系固定下来,避免每天靠记忆找端口。如果已有进程占住端口,先查进程再决定要不要终止,不建议 pkill 直接盲杀,容易把别的服务一起带走。

5.5 多页面相互污染:切到另一个页面,旧页面选的值被带过去

现象:集合里有两个页面都有“选择区域”这个下拉框,在 A 页选了“华东”,切到 B 页后 B 页也自动变成“华东”,看起来像数据串了门。

原因:Streamlit 的多页面应用共享同一个 session_state,widget 的 key 在整个应用里是全局命名空间。两个页面用了相同的 key,值就被后者覆盖或继承。

解决:每个页面的 widget key 加上页面前缀,比如 dashboard_region 和 analysis_region。这个习惯要在一开始就建立,等页面数量多了再改 key,会连带丢失用户的选中状态,排查成本更高。集合的多页面资源里已经按这个规范命名,直接照搬就行。

6. 把集合变成自己的:部署前的三个收尾技巧

模板拿回来能用只是第一步,真正变成自己手里的工具,还得处理路径、配置优先级和上线前验证。这三件事拼起来,决定这份集合在别人电脑上能不能也顺利跑起来。

6.1 把路径写成一个入口函数

集合里每个页面开头那段路径字符串,我见过太多硬编码写法。最稳的方式是用 pathlib 统一管理:

from pathlib import Path BASE_DIR = Path(__file__).resolve().parent DATA_DIR = BASE_DIR / "data"

用 Path 拼接而不是字符串加号,Windows 和 Linux 通用,项目整体移动或换机器时不需要改任何一行代码。数据文件名也统一放到常量里,别在代码里散落一堆魔法字符串。

6.2 配置文件默认值与命令行优先级

部署到服务器时,配置来源会同时存在,优先级需要心里有底:

配置来源优先级生效时机
Streamlit 默认配置最低每次启动
config.toml中等重启后生效
命令行参数最高当前启动

这个优先级意味着:config.toml 里写了 port=8502,同时命令行带 --server.port=8501,以命令行为准。我习惯统一把端口写进 config.toml,命令行只放身份相关的东西,这样换机器时改一个文件就行。

6.3 验证:启动后先走一遍主要交互

任何页面改完上线前,我都强制自己走一遍完整交互链路:打开页面填筛选、点提交、导出文件、下载后打开检查编码,四个动作缺一不可。特别是导出文件,一定要本地打开确认中文正常,别等交付到同事手里才发现乱码。

第一次拆这种集合资源时,我拿到手第一件事不是读代码,而是先把每个页面跑起来,再对照代码看哪些套路是重复的。正是那次被动式的对比,让我确认“先跑通,再看代码,最后改需求”才是处理模板类项目最靠谱的流程。从那以后,每个模板项目的第一天我都强制走一遍:建虚拟环境、装依赖、启动、打开浏览器点完主流程,再决定从哪里下手改。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询