- Python
Streamlitとは・使い方入門|再実行とキャッシュ効果を実測で解説
結論
Streamlitは、Pythonのスクリプトを書くだけで、表・グラフ・入力欄のあるWebアプリを作れるフレームワークです。pip install streamlit で入れ、streamlit run app.py で起動します。ボタンを押すたびにスクリプトが上から再実行される仕組みを知ると、キャッシュとセッション状態の使い方で迷わなくなります。
この記事でわかること
- インストールから起動・ウィジェット・グラフ・ページ分けまでの使い方
- 操作ごとの再実行の回数と、st.cache_data で1,019msが1msになった実測
- よく出るエラーの原文と直し方、Gradio・Dashとの選び方
Streamlitのアプリは、ブラウザで操作が起きるたびに、サーバー側でスクリプト全体がもう一度実行されます。この記事は、この動きを実際に計測しながら使い方を説明します。
flowchart TD
A[ブラウザで操作] --> B[WebSocketで<br>サーバーへ通知]
B --> C[スクリプトを<br>上から再実行]
C --> D{cache_dataに<br>結果がある?}
D -->|ある| E[計算を飛ばす]
D -->|ない| F[関数を実行]
E --> G[差分を画面へ]
F --> G
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| Streamlitとは | PythonだけでデータのWebアプリを作るオープンソースのフレームワーク | Streamlitとは |
| Streamlit インストール | venvを作って pip install streamlit。Python 3.11以上 |
インストール |
| Streamlit 使い方(起動) | streamlit run app.py。既定のポートは8501 |
起動と設定 |
| ボタンを押すと値が消える | スクリプトが再実行されるため。st.session_state に置く | 再実行モデル・session_state |
| Streamlitが重い | st.cache_data・st.cache_resource・st.form・st.fragment | キャッシュ・form と fragment |
| 複数ページにしたい | pages フォルダーか st.navigation | ページ分け |
| 公開(デプロイ)したい | Community Cloud(無料)・Docker・Snowflake など | デプロイ |
| Gradio・Dashとの違い | 再実行の単位と得意な用途が違う | Gradio・Dashとの違い |
確認環境:Ubuntu 24.04.5 LTS の上で、Python 3.13.18 の venv(/dev/shm の作業場所)に Streamlit 1.66.1 を pip で入れました(同時に入った版は pandas 3.1.0・numpy 2.5.4・pyarrow 26.0.0・altair 6.3.0・starlette 1.10.0・uvicorn 0.54.0)。最初の import より前に、環境変数 STREAMLIT_BROWSER_GATHER_USAGE_STATS=false と作業場所の .streamlit/config.toml で利用統計の送信を止め、127.0.0.1 のポート 48533 で起動しました。画面の操作は Chromium 153.0.8010.12(playwright 1.63.0・Node.js 22.23.4)で行い、再実行の回数と時間はアプリが書き出すログで、サーバーのメモリは ps の RSS で計りました。エラーの原文は streamlit.testing.v1.AppTest と CLI で出しています。Community Cloud・Docker・Snowflake でのデプロイ、設定の優先順位は Streamlit の公式ドキュメント、Gradio・Dash の版は PyPI で確かめました。図は mermaid で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Streamlitとは・使い方入門|再実行とキャッシュ効果を実測で解説」
Streamlitとは(できること・仕組み)
この節では、Streamlitが何をするソフトで、どんな仕組みで動くのかが分かります。
Streamlitは、データ分析や機械学習の結果を、HTML・CSS・JavaScriptを書かずにWebアプリにできるPythonのフレームワークです。 ライセンスは Apache-2.0 で、2026年10月29日時点の最新版は 1.66.1(PyPI への公開は2026年10月21日)です。公式のリリースノートでは、1.66.0 が2026年10月14日に出ています。
表とグラフ
pandas の DataFrame を st.dataframe にそのまま渡すと、並べ替えのできる表になる。
社内の集計画面
入力欄とボタン
スライダー・選択肢・テキスト・ファイルの受け取りを1行ずつ書ける。
条件を変えて試す道具
機械学習のデモ
学習したモデルを読み込み、入力を変えて予測を見せる。
PoC・説明用
LLM のチャット画面
st.chat_input と st.chat_message で会話の画面を作れる。
社内向けの試作
仕組み:1つのPythonプロセスと、ブラウザごとのセッション
公式ドキュメントは、Streamlitのサーバーは Starlette の上に作られ Uvicorn で動き、ブラウザとは WebSocket でつながる、と説明しています。手元でも、起動ログは Uvicorn server started on 127.0.0.1:48533、応答ヘッダーは server: uvicorn で、ブラウザは ws://127.0.0.1:48533/_stcore/stream に WebSocket を開きました。ブラウザのタブごとに別のセッションができ、計算はすべてサーバー側の1つのプロセスが行います。
/_stcore/health が ok を返すまでStreamlitのインストール(venv・pip・hello・init)
この節では、Streamlitを仮想環境に入れる手順と、入ったかどうかの確かめ方が分かります。
Streamlit 1.66.1 は Python 3.11 以上が必要です(PyPI の Requires-Python が >=3.11)。pandas・pyarrow など36個のパッケージ(pip を含む)が一緒に入り、venv は 392MB になりました。プロジェクトごとに venv を作って入れます。
venv を作って有効にする
python3 -m venv .venv のあと、Linux・Mac は source .venv/bin/activate、Windows は .venv\Scripts\activate。
利用統計の送信を止める
import や起動の前に、環境変数か .streamlit/config.toml を用意する(次の節)。
pip で入れる
pip install streamlit。uv を使うなら uv add streamlit。
版を確かめる
streamlit version で Streamlit, version 1.66.1 と出れば入っている。streamlit hello で見本のアプリが開く。
streamlit --help に出たサブコマンドです。
Commands:
activate Activate Streamlit by entering your email.
cache Manage the Streamlit cache.
config Manage Streamlit's config settings.
docs Look up a Streamlit command, or open the docs in your browser.
hello Runs the Hello World script.
help Print this help message.
init Initialize a new Streamlit project.
run Run a Python script, piping stderr to Streamlit.
skills Install Streamlit AI-agent skills.
version Print Streamlit's version number.
streamlit init demo を実行すると、demo/streamlit_app.py と demo/requirements.txt(中身は streamlit の1行)ができ、「Run the app now? [Y/n]」と聞かれました。雛形から始めたいときに使えます。venv の作り方の詳細は関連記事「Python venvの使い方|仮想環境の作成・有効化・削除をOS別にコピペ」で、uv での管理は関連記事「uv venvとuvの使い方|仮想環境の作成・有効化とpipとの速さ比較」で扱っています。
streamlit runで起動する(ポート・利用統計・config.toml)
この節では、アプリの起動のしかたと、ポートやアドレス、利用統計の送信を設定ファイルで決める方法が分かります。
起動は streamlit run ファイル名.py です。 python app.py では画面は出ません。既定ではポート 8501 で待ち受け、ブラウザが自動で開きます。手元ではアドレスとポートを指定して起動しました。
$ streamlit run streamlit_app.py --server.port 48533
2026-10-29 20:22:44.283 Uvicorn server started on 127.0.0.1:48533
You can now view your Streamlit app in your browser.
URL: http://127.0.0.1:48533
$ curl -sv http://127.0.0.1:48533/_stcore/health
< HTTP/1.1 200 OK
< server: uvicorn
< content-type: text/plain; charset=utf-8
< set-cookie: _streamlit_xsrf=2|...; Path=/; SameSite=Lax
ok
利用統計(テレメトリー)を送らない設定
公式の設定の一覧では、browser.gatherUsageStats は「Whether to send usage statistics to Streamlit.」で、既定は true(送る)です。社内のデータを扱うときや、外へ通信させたくないときは、最初の起動より前に false にします。手元では次の2つを両方入れました。
# 1. 環境変数(シェルや CI で)
export STREAMLIT_BROWSER_GATHER_USAGE_STATS=false
export STREAMLIT_SERVER_HEADLESS=true
# 2. プロジェクトの .streamlit/config.toml(streamlit run を実行するフォルダー)
[browser]
gatherUsageStats = false
[server]
headless = true
address = "127.0.0.1"
効いているかは streamlit config show で確かめます。
$ streamlit config show | grep -B4 '^gatherUsageStats'
# Default: true
#
# The value below was set in command-line argument or environment variable
gatherUsageStats = false
この設定で Chromium から操作した間の通信は、292件すべてが 127.0.0.1:48533 宛てでした。
設定の決まり方と、よく変える項目
公式ドキュメントによると、設定ファイルはホームの ~/.streamlit/config.toml(Windows は %userprofile%/.streamlit/config.toml)と、作業フォルダーの .streamlit/config.toml を組み合わせ、作業フォルダーの方が優先されます。さらに環境変数とコマンドラインの指定で上書きできます。streamlit config show で見た既定値です。
| 項目 | 既定値 | 変える場面 |
|---|---|---|
server.port |
8501 | 複数のアプリを同時に動かす |
server.address |
(未設定) | 手元だけで使うなら 127.0.0.1 |
server.maxUploadSize |
200(MB) | 大きなファイルを受け取る |
server.runOnSave |
false | 保存したら自動で再実行したい |
server.enableXsrfProtection |
true | 原則そのまま |
client.showErrorDetails |
full | 公開時はトレースバックを見せない設定にする |
browser.gatherUsageStats |
true | 送信を止めるなら false |
Streamlitの使い方:表示とレイアウト(表・グラフ・サイドバー)
この節では、文字・表・グラフ・状態表示の出し方と、サイドバーや列で画面を分ける方法が分かります。
出したいものを、上から順に st.○○ で書くと、その順に画面に並びます。 手元で動かした検証用アプリの中心です。
import time
import numpy as np
import pandas as pd
import streamlit as st
st.title("売上ダッシュボード(検証用)")
@st.cache_data
def load_sales(n: int) -> pd.DataFrame:
time.sleep(1.0) # 重い読み込みの代わり
rng = np.random.default_rng(0)
return pd.DataFrame({
"日付": pd.date_range("2026-10-01", periods=n, freq="D"),
"売上": rng.integers(80, 160, n),
"店舗": rng.choice(["東京", "大阪", "名古屋"], n),
})
with st.sidebar: # 左のサイドバー
days = st.slider("日数", 7, 28, 14)
stores = st.multiselect("店舗", ["東京", "大阪", "名古屋"], default=["東京", "大阪"])
df = load_sales(days)
df = df[df["店舗"].isin(stores)]
col1, col2 = st.columns(2) # 横に2列
col1.metric("合計売上", int(df["売上"].sum()))
col2.metric("件数", len(df))
st.line_chart(df, x="日付", y="売上") # 折れ線グラフ
st.dataframe(df, hide_index=True) # 表
Chromium で開くと、左にサイドバー(スライダー・複数選択)、右に大きな見出し、2列の数値、折れ線グラフ、表が並びました。右上には「Deploy」のボタンが出ます。日数を15に変えると、合計売上は946になりました。
| 出したいもの | 主な関数 | メモ |
|---|---|---|
| 文字 | st.title・st.header・st.markdown・st.caption・st.code・st.write |
st.write は渡した型に合わせて表示 |
| 表 | st.dataframe・st.table・st.data_editor |
data_editor は画面で編集できる |
| 数値 | st.metric |
前回との差も出せる |
| グラフ | st.line_chart・st.bar_chart・st.area_chart・st.pyplot・st.plotly_chart・st.altair_chart・st.map |
簡単なものは Altair を使う組み込み関数 |
| 画像・音声・動画 | st.image・st.audio・st.video |
|
| 状態の表示 | st.success・st.info・st.warning・st.error・st.spinner・st.progress・st.toast |
処理中の表示は spinner |
| 画面の分割 | st.sidebar・st.columns・st.tabs・st.expander・st.container |
with の中に書く |
表・タブ・棒グラフ・状態表示・toast を1つのスクリプトに書いて AppTest で動かすと、例外は0件で、タブ2つと toast の「保存しました」が出ていました。表やグラフに use_container_width=True を付けると、次の警告が出ました。
Please replace `use_container_width` with `width`.
`use_container_width` will be removed after 2025-12-31.
For `use_container_width=True`, use `width='stretch'`. For `use_container_width=False`, use `width='content'`.
以前の記事のコードを写すときは、width="stretch" に置き換えます。DataFrame の作り方・絞り込みは関連記事「pandasの使い方 逆引き81選|pandas 3.0の実行結果つき」で、st.pyplot に渡すグラフの日本語の文字化けは関連記事「matplotlibの使い方と日本語文字化け対策|OS別・Colab・3.12対応」で扱っています。
ウィジェットとファイルアップロードの使い方
この節では、入力欄・ボタン・ファイルの受け取りの書き方と、戻り値の扱いが分かります。
ウィジェットは「今の値」を戻り値で返します。 ボタンは押された直後の1回だけ True を返し、次の再実行では False に戻ります。
name = st.text_input("名前", key="name")
if st.button("あいさつ", key="hello"):
st.success(f"こんにちは、{name or '名無し'}さん")
up = st.file_uploader("CSVを選ぶ", type=["csv"], key="up")
if up is not None:
udf = pd.read_csv(up) # 中身はメモリ上のファイルとして渡る
st.write(f"{up.name}: {len(udf)}行 {len(udf.columns)}列")
st.dataframe(udf)
Chromium で「名前」に「山田」と入れて「あいさつ」を押すと「こんにちは、山田さん」が出ました。3行のCSVを選ぶと「sample.csv: 3行 3列」と表が出ました。受け取れる大きさの上限は既定で200MB(server.maxUploadSize)です。
| 種類 | 関数 | 戻り値 |
|---|---|---|
| ボタン | st.button・st.download_button |
押した直後だけ True |
| オン・オフ | st.checkbox・st.toggle |
True・False |
| 1つ選ぶ | st.radio・st.selectbox・st.select_slider |
選んだ値 |
| 複数選ぶ | st.multiselect |
リスト |
| 数値・日付 | st.slider・st.number_input・st.date_input・st.time_input |
数値・date・time |
| 文字 | st.text_input・st.text_area・st.chat_input |
文字列(未入力は空文字) |
| ファイル | st.file_uploader・st.camera_input |
未選択は None |
Streamlitの再実行モデル(ボタンを押すとスクリプトが上から動く)
この節では、Streamlit でいちばん大事な「操作のたびにスクリプト全体が再実行される」動きを、実測のログで確かめられます。
ウィジェットを操作すると、Streamlit はスクリプトを1行目からもう一度実行し、新しい結果で画面を描き直します。 検証用アプリの最後に、実行ごとに時刻・回数・読み込み時間をファイルへ書く処理を入れ、Chromium で操作しました。
20:23:13.657 run=1 days=14 cache=True load_ms=1019 total_ms=2203 ← 最初に開いた
20:23:14.632 run=1 days=14 cache=True load_ms=1 total_ms=80 ← 再読み込み(新しいセッション)
20:23:15.411 run=2 days=14 cache=True load_ms=1 total_ms=77 ← 名前を入れて Enter
20:23:16.381 run=3 days=14 cache=True load_ms=2 total_ms=175 ← あいさつボタン
20:23:16.832 run=4 days=14 cache=True load_ms=1 total_ms=98 ← カウント+1
20:23:17.378 run=5 days=14 cache=True load_ms=1 total_ms=112 ← カウント+1
20:23:17.888 run=6 days=14 cache=True load_ms=1 total_ms=84 ← カウント+1
20:23:19.372 run=7 days=15 cache=True load_ms=1003 total_ms=1111 ← スライダーを15へ
| 操作 | 再実行の回数 | 1回の時間 |
|---|---|---|
| ページを開く(新しいセッション) | 1 | 2,203ms(初回)・80ms(2回目以降) |
| テキスト入力で Enter | 1 | 77ms |
| ボタンを押す | 1 | 84〜175ms |
| スライダーを動かす(新しい値) | 1 | 1,111ms(キャッシュに無い値) |
| ファイルを選ぶ | 1 | 65ms |
| フォームの中で入力(送信前) | 0 | ― |
初めて開いたときは、ブラウザに見出しと「実行 1 回目」が出るまで5,579ms かかりました(1秒の読み込みを含む)。どの操作でも、画面の一部だけでなくスクリプト全体が1回動きます。 そのため、重い処理をそのまま書くと、ボタンを押すたびに待たされます。これを避けるのが、次の3つの節の session_state・キャッシュ・form と fragment です。
普通の変数で数える
count = 0 のあと count += 1。再実行のたびに0から始まり、何回押しても1のままst.session_state に置く。同じセッションの再実行をまたいで値が残る手元で3回押した結果:session_state は3、普通の変数は1
st.session_stateで値を保つ(セッション状態)
この節では、再実行をまたいで値を残す st.session_state の使い方と、ページ移動での残り方が分かります。
st.session_state は、ブラウザのタブ(セッション)ごとの辞書です。 最初に「無ければ入れる」形で初期化してから使います。
if "count" not in st.session_state: # 初期化は「無ければ」
st.session_state.count = 0
if st.button("カウント+1", key="inc"):
st.session_state.count += 1
plain = 0
plain += 1
st.write("session_state.count =", st.session_state.count, "/ 普通の変数 =", plain)
session_state.count = 3 / 普通の変数 = 1
- ウィジェットに
keyを付けると、その値はst.session_state[key]でも読めます。 手元の AppTest でもat.session_state["count"] == 2を確かめられました。 - ブラウザで再読み込みすると新しいセッションになり、回数は1に戻りました(ログの run=1)。
- サイドバーのページ一覧から2ページ目へ移ると、count は4のまま残っていました。公式ドキュメントは、Markdown のリンクでページへ移ると新しいセッションになり session_state が消える、と書いています。ページ移動は
st.page_linkやst.switch_pageを使います。 - ウィジェットを作ったあとに、同じ key の値を書き換えるとエラーになります(エラーの節)。書き換えたいときは、ウィジェットより前に代入するか、
on_click・on_changeのコールバックの中で行います。
st.cache_dataとst.cache_resourceの違い(キャッシュの効果を実測)
この節では、2種類のキャッシュの違いと、手元で計った効果が分かります。
データを返す関数は @st.cache_data、モデルやDB接続のように1つを使い回すものは @st.cache_resource を付けます。 1秒待つ関数で読み込み時間を計りました。
| 状況 | 読み込み時間 | 理由 |
|---|---|---|
| cache_data あり・初回(days=14) | 1,019ms | 関数が動いた |
| cache_data あり・2回目以降(days=14) | 1〜2ms | 結果を再利用 |
| cache_data あり・引数が変わった(days=15) | 1,003ms | 引数ごとに別のキャッシュ |
| キャッシュなし・ボタンを押した | 1,001ms(毎回) | 再実行のたびに関数が動く |
| 別のタブ(新しいセッション) | 1ms | キャッシュはセッションをまたいで共有 |
5つのタブで同時に開いても、サーバーの RSS は 214,024KB から 215,020KB へ約1MB 増えただけでした。キャッシュはプロセスのメモリにあるため、サーバーを止めると消えます。
cache_data はコピー、cache_resource は同じ物
@st.cache_data(ttl=3) # 3秒で期限切れ
def fetch_rate():
time.sleep(0.5)
return {"rate": 150.0, "at": datetime.datetime.now().strftime("%H:%M:%S")}
@st.cache_resource
def get_model():
time.sleep(0.5)
return {"weights": [1, 2, 3]}
d1 = fetch_rate(); d1["rate"] = -1 # 戻り値を書き換えても
d2 = fetch_rate() # 次はコピーが返る
m1 = get_model(); m2 = get_model()
cache_data: 同じ物? False / 2回目の rate=150.0 取得時刻=20:24:35
cache_resource: 同じ物? True
(3.5秒後に再実行)
cache_data: 同じ物? False / 2回目の rate=150.0 取得時刻=20:24:42
cache_data は呼ぶたびにコピーを返すため、戻り値を書き換えても次の呼び出しに影響しませんでした。cache_resource は同じオブジェクトを返します。ttl を3秒にすると、3.5秒後の再実行で関数がもう一度動き、取得時刻が変わりました。
st.cache_data
DataFrame・API の結果・計算の結果
呼ぶたびにコピー(pickle で保存)
ttl・max_entries で期限切れや溢れた結果を捨てる
st.cache_resource
機械学習のモデル・DB 接続・クライアント
全セッションで同じ1つを共有
スレッドから同時に使われても安全な物にする
引数に DB 接続のようなハッシュできない物を渡すとエラーになります。公式ドキュメントのとおり、引数名の先頭に _ を付ける(_conn)と、その引数はキャッシュのキーに使われません。キャッシュを消すには load_sales.clear()・st.cache_data.clear()・st.cache_resource.clear() を呼ぶか、streamlit cache clear を実行します。
st.formとst.fragmentで再実行を減らす
この節では、入力のたびに再実行させない st.form と、画面の一部だけを再実行する st.fragment の効果が分かります。
with st.form("f"):
a = st.text_input("フォーム内の入力")
ok = st.form_submit_button("送信")
if ok:
st.write("送信された:", a)
@st.fragment
def part():
if st.button("フラグメントのボタン"):
st.write("フラグメントだけ再実行")
part()
st.button("ページのボタン")
ページ全体の実行(page run)と、フラグメントの実行(fragment run)を別々に記録した結果です。
| 操作 | ページ全体の実行 | フラグメントの実行 |
|---|---|---|
| フォームの中で入力して欄を離れる | 0 | 0 |
| フォームの「送信」 | 1 | 1 |
| フラグメントの中のボタン | 0 | 1 |
| フラグメントの外のボタン | 1 | 1 |
- 1重い読み込みは cache_data に入れる手元では1,019msが1msになった
- 2入力欄が多い画面は st.form でまとめる送信を押すまで再実行が0回だった
- 3一部だけ更新する部品は st.fragment に入れる中のボタンではページ全体が動かなかった
- 4定期更新は st.fragment(run_every=…)公式ドキュメントによると、指定した間隔でフラグメントだけを再実行する
公式ドキュメントは、キャッシュとフラグメントを同じ関数に付けられないこと、フラグメントの再実行では戻り値が使われないため共有する値は session_state に置くこと、を制限として挙げています。
Streamlitのページ分け(マルチページアプリ)
この節では、アプリを複数のページに分ける2つの方法と、ファイル名とURLの関係が分かります。
入口のファイルの隣に pages フォルダーを作り、.py を置くと、そのままページになります。 手元では pages/2_about.py を置くと、サイドバーに「streamlit app」と「about」が並び、URL は http://127.0.0.1:48533/about になりました。先頭の数字は並び順に使われ、表示名とURLからは外れます。
app/
├── streamlit_app.py ← 入口(streamlit run で指定)
├── .streamlit/config.toml
└── pages/
├── 2_about.py ← /about
└── 3_lab.py ← /lab
公式ドキュメントは、より細かく決められる st.Page と st.navigation を勧めています。
import streamlit as st
pg = st.navigation([
st.Page("dashboard.py", title="売上", icon=":material/bar_chart:"),
st.Page("settings.py", title="設定", icon=":material/settings:"),
])
pg.run()
| 方法 | 向いている場面 | メモ |
|---|---|---|
| pages フォルダー | 数ページの小さなアプリ | ファイルを置くだけ。順番はファイル名の数字 |
| st.Page と st.navigation | ログイン状態でページを出し分ける・グループ分け | 公式が勧める方法。入口がページの一覧を持つ |
Streamlitのよくあるエラーと直し方(原文つき)
この節では、Streamlit でよく出るエラーの原文と、原因・直し方が分かります。
| 出た文言(原文) | 原因 | 直し方 |
|---|---|---|
bash: streamlit: command not found |
venv が有効になっていない | venv を有効にする。python -m streamlit run でも起動できる |
ModuleNotFoundError: No module named 'streamlit' |
入れた環境と違う Python で動かした | venv の python で動かす |
missing ScriptRunContext! This warning can be ignored when running in bare mode. |
python app.py で動かした |
streamlit run app.py で起動する |
Port 48533 is not available |
そのポートを別のアプリが使っている | 前のアプリを止めるか --server.port を変える |
Error: Invalid value: File does not exist: nothing.py |
ファイル名かフォルダーの誤り | 今いるフォルダーとファイル名を確かめる |
StreamlitDuplicateElementId |
同じ種類・同じ引数のウィジェットが2つある | それぞれに別の key を付ける |
StreamlitDuplicateElementKey |
同じ key を2回使った |
key を重ならない名前にする |
st.session_state has no attribute "count" |
初期化の前に読んだ | if "count" not in st.session_state: で先に入れる |
StreamlitWidgetAlreadyInstantiatedError |
ウィジェットを作ったあとに同じ key を書き換えた | 作る前に代入するか、コールバックで書き換える |
UnhashableParamError: Cannot hash argument 'conn' |
cache_data の引数にハッシュできない物 | 引数名を _conn にする。接続自体は cache_resource へ |
Cannot serialize the return value (of type `_thread.lock`) |
cache_data の戻り値を pickle できない | その関数は cache_resource にする |
同じボタンを2つ書いたときの全文です。
streamlit.errors.StreamlitDuplicateElementId: There are multiple `button` elements with the same auto-generated ID. When this element is created, it is assigned an internal ID based on the element type and provided parameters. Multiple elements with the same type and parameters will cause this error.
To fix this error, please pass a unique `key` argument to the `button` element.
初期化の前に session_state を読んだときと、ウィジェットのあとで値を書き換えたときの全文です。
AttributeError: st.session_state has no attribute "count". Did you forget to initialize it? More info: https://docs.streamlit.io/develop/concepts/architecture/session-state#initialization
streamlit.errors.StreamlitWidgetAlreadyInstantiatedError: `st.session_state['name']` cannot be modified after the widget with that key is instantiated. Assign `st.session_state['name']` before creating the widget, or update it from an `on_change` or `on_click` callback, which runs before the widget is instantiated.
python streamlit_app.py で動かしたときは、Session state does not function when running a script without `streamlit run` という警告も出て、画面は開きませんでした。
Streamlitのデプロイ(Community Cloud・Docker)と公開前のテスト
この節では、作ったアプリを公開する選択肢と、公開前に自動テストする方法が分かります。
| 公開先 | 特徴(公式ドキュメント) | 向いている場面 |
|---|---|---|
| Streamlit Community Cloud | 無料。GitHub の公開・非公開リポジトリとつなぐ。Debian 11 で動く。セキュリティ更新中の Python だけ使える | 個人・公開してよいデモ |
| Docker | 公式の例は python:3.12-slim・EXPOSE 8501・/_stcore/health の HEALTHCHECK |
社内サーバー・クラウドのコンテナ |
| Kubernetes | 公式の手順あり | 既に Kubernetes を使っている |
| Snowflake | Snowflake の中で Streamlit を動かす | データが Snowflake にある |
| Google Cloud Run など | 公式の「Other platforms」に案内がある | 各クラウドを使っている |
Docker の公式の例は、見本のリポジトリを git clone して使います。次は、その例をもとに、手元のファイルを COPY する形に置き換えたものです(HEALTHCHECK で使う curl を公式の例と同じく apt で入れています)。
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
COPY . .
RUN pip3 install -r requirements.txt
EXPOSE 8501
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
コンテナの中では --server.address=0.0.0.0 にして外から届くようにします。手元のように自分だけで使うときは 127.0.0.1 にします。HEALTHCHECK の /_stcore/health は、手元でも 200 OK と ok を返しました。Community Cloud では、GitHub への更新の反映は1分に5回までに制限されています。
AppTest で画面を開かずにテストする
streamlit.testing.v1.AppTest を使うと、ブラウザなしでスクリプトを動かし、ウィジェットを操作して結果を確かめられます。
from streamlit.testing.v1 import AppTest
def test_count_button():
at = AppTest.from_file("streamlit_app.py").run(timeout=10)
assert not at.exception
at.button(key="inc").click().run()
at.button(key="inc").click().run()
assert at.session_state["count"] == 2
def test_hello():
at = AppTest.from_file("streamlit_app.py").run(timeout=10)
at.text_input(key="name").input("佐藤")
at.button(key="hello").click().run()
assert at.success[0].value == "こんにちは、佐藤さん"
$ python -m pytest -q test_app.py
.. [100%]
2 passed in 2.78s
StreamlitとGradio・Dash・Flaskの違い(判断表)
この節では、似た道具との違いと、どれを選べばよいかが分かります。
Streamlit は「スクリプト全体を再実行」、Gradio と Dash は「イベントに結びつけた関数だけを実行」するのが大きな違いです。 版は2026年10月29日時点の PyPI の値です。
| Streamlit | Gradio | Dash | |
|---|---|---|---|
| 最新版 | 1.66.1 | 6.31.0 | 4.4.1 |
| Python | 3.11以上 | 3.10以上 | 3.9以上 |
| ライセンス | Apache-2.0 | Apache-2.0 | MIT |
| 動き方 | 操作のたびにスクリプト全体を再実行 | 入力→関数→出力を結びつける | コールバック(入力と出力を宣言) |
| 得意なこと | データの画面・社内ツール・試作 | 機械学習モデルのデモ・API 化 | 細かく作り込むダッシュボード |
| 無料の公開先 | Community Cloud | Hugging Face Spaces | 自分で用意する |
- 1表・グラフ・絞り込みの画面を早く作りたい → Streamlitpandas の結果をそのまま見せられる
- 2モデルに入力して出力を見るデモ → Gradio画像・音声の入出力の部品が多い
- 3部品ごとに細かく更新する大きなダッシュボード → DashPlotly のグラフを中心に組む
- 4ログイン・権限・多数の利用者がいる本番の Web サービス → Flask・FastAPI・Django画面と API を自由に設計する
1向いている
分析結果の共有、社内の小さな道具、機械学習やLLMの試作、条件を変えて見比べる画面。
2向いていない
画面の細かなデザインが必要なサービス、同時に多数の人が使う重いアプリ、SEO が必要な公開ページ。
3迷ったら
まず Streamlit で作って使い道を確かめ、利用者や要件が増えたら Web フレームワークへ移す。
Gradio の使い方と、実行の仕組みの比較は関連記事「Gradioとは|使い方・API呼び出し・Spaces公開をPythonで実演」で、API を作る側は関連記事「FastAPI入門|uvで作るCRUD API完成コードと実行結果」で扱っています。
よくある質問
Q. Streamlitとは何ですか? A. Python のスクリプトだけで、表・グラフ・入力欄のある Web アプリを作れるオープンソースのフレームワークです。HTML や JavaScript を書かずに、データ分析の結果を画面にできます。
Q. Streamlitは無料ですか? A. 本体は Apache-2.0 のオープンソースで無料です。公開先の Streamlit Community Cloud も、公式ドキュメントでは無料と書かれています。
Q. どの Python の版で使えますか? A. 1.66.1 は Python 3.11 以上です。手元では 3.13.18 で動かしました。
Q. ボタンを押すと、前に入れた値や結果が消えます。
A. 操作のたびにスクリプト全体が再実行され、普通の変数は初期値に戻るためです。残したい値は st.session_state に入れます。
Q. 動きが重いです。
A. データの読み込みや計算に @st.cache_data を付けます。手元では1,019msが1msになりました。入力欄の多い画面は st.form、一部だけ更新したい部分は st.fragment を使います。
Q. ポートを変えるには?
A. streamlit run app.py --server.port 8502 か、.streamlit/config.toml の [server] に port = 8502 と書きます。
Q. 利用統計の送信を止めるには?
A. .streamlit/config.toml の [browser] に gatherUsageStats = false と書くか、環境変数 STREAMLIT_BROWSER_GATHER_USAGE_STATS=false を設定します。streamlit config show で反映を確かめられます。
Q. 同じネットワークの別のPCから見るには?
A. --server.address 0.0.0.0 で待ち受け、そのPCのIPアドレスとポートで開きます。社外に見せない場合は、ファイアウォールや認証の仕組みを手前に置きます。
Q. 複数のページに分けられますか?
A. 入口のファイルの隣に pages フォルダーを作って .py を置くか、st.Page と st.navigation を使います。公式は後者を勧めています。
Q. Jupyter Notebook の中で動かせますか?
A. Streamlit は streamlit run でサーバーを起動して動かす仕組みです。Notebook で試したコードを .py に移して起動します。
Q. ログイン機能を付けられますか?
A. 公式ドキュメントによると、OpenID Connect(OIDC)によるユーザー認証に対応しており、st.login() で認証の提供元へ移り、ログイン後は st.user で利用者の情報を読めます。権限を細かく分けたいサービスは、Web フレームワークで作る方が向いています。
まとめ
- Streamlit は Python のスクリプトだけでデータの Web アプリを作るフレームワーク。1.66.1 は Python 3.11 以上で、venv は 392MB になった。
- 起動は
streamlit run app.py。利用統計は既定で送る設定なので、社内で使うときは最初の起動前にgatherUsageStats = falseにする。手元では通信が 127.0.0.1 だけになった。 - 操作のたびにスクリプト全体が1回再実行される。値は
st.session_stateに置く。 - 重い処理は
st.cache_data(データ)・st.cache_resource(モデル・接続)。手元では1,019msが1msになり、キャッシュはセッションをまたいで共有された。st.formとst.fragmentで再実行の範囲を減らせる。 - 公開は Community Cloud(無料)・Docker・Snowflake など。公開前に AppTest でテストでき、よくあるエラーは key の重複と session_state の初期化忘れ。
参考・出典
確認日はいずれも 2026年10月29日です。
- Streamlit「Streamlit documentation」:https://docs.streamlit.io/
- Streamlit「config.toml」:https://docs.streamlit.io/develop/api-reference/configuration/config.toml
- Streamlit「Understanding Streamlit's client-server architecture」:https://docs.streamlit.io/develop/concepts/architecture/architecture
- Streamlit「Caching overview」:https://docs.streamlit.io/develop/concepts/architecture/caching
- Streamlit「Working with fragments」:https://docs.streamlit.io/develop/concepts/architecture/fragments
- Streamlit「Overview of multipage apps」:https://docs.streamlit.io/develop/concepts/multipage-apps/overview
- Streamlit「Release notes」:https://docs.streamlit.io/develop/quick-reference/release-notes
- Streamlit「Streamlit Community Cloud」:https://docs.streamlit.io/deploy/streamlit-community-cloud
- Streamlit「Status and limitations of Community Cloud」:https://docs.streamlit.io/deploy/streamlit-community-cloud/status
- Streamlit「Deploy Streamlit using Docker」:https://docs.streamlit.io/deploy/tutorials/docker
- PyPI「streamlit」:https://pypi.org/project/streamlit/
- PyPI「gradio」:https://pypi.org/project/gradio/
- PyPI「dash」:https://pypi.org/project/dash/
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Streamlitとは・使い方入門|再実行とキャッシュ効果を実測で解説」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。