Streamlitを静的サイトで動かせるstliteというライブラリがあるらしいです。
簡単に試してみたのでメモしておきます。
Streamlitとは
StreamlitはPythonだけで簡単にWebアプリを作るライブラリです。
よくある使い方としてはダッシュボードとして、ユーザーの入力に応じてフィルタリングができる動的なグラフや表の表示などに使われます。
Rでいうと{shiny}パッケージのようなものです。
例えば、
import streamlit as st name = st.text_input('Your name') st.write("Hello,", name or "world")
というコードを書いて(仮にtest_streamlit.py
というファイル名にするとします)、シェルで
$ streamlit run test_streamlit.py
を実行するとローカルでサーバが建てられて、streamlitのアプリにlocalhostからアクセスできるようになります。
上記のコードだと、次の画像のようなものが出来上がります。
stliteとは
stliteはStreamlitを静的サイト(例えば1つのhtmlファイル)で動かせるようにするものです。
WebAssemblyを用いてクライアントサイドでPythonを動かすPyodideを使っているらしいです。
例えば、次のようなhtmlファイルを作ります(※このコードは公式のサンプルコードからお借りしました)。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <title>stlite app</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.39.0/build/stlite.css" /> </head> <body> <div id="root"></div> <script src="https://cdn.jsdelivr.net/npm/@stlite/mountable@0.39.0/build/stlite.js"></script> <script> stlite.mount( ` import streamlit as st name = st.text_input('Your name') st.write("Hello,", name or "world") `, document.getElementById("root") ); </script> </body> </html>
上記htmlでは、CDNからstliteを読み込んでいます。そしてstlite.mount()
の部分に入れたPythonコードが実行され、streamlitが表示されます。
このhtmlファイルをブラウザで開くと、次の画像のような画面が表示されます。
最初に数秒の読み込みが入りますが、最終的には同様のstreamlitアプリが表示されています。
他の使用例
APIを呼び出す簡単なアプリを試してみようと思い、天気予報 API(livedoor 天気互換)を使って指定されたエリアの天気予報を表示するアプリを作ってみました。
使用したコードは以下のものになります。
import streamlit as st import json from pyodide.http import open_url # NOTE: stliteではrequestsやurllibは使えないためこちらを使う AREA_ID_DICT = {'稚内': '011000', '旭川': '012010',...} # 長いので一部のみ載せています def get_weather(area_id): url = f"https://weather.tsukumijima.net/api/forecast?city={area_id}" res = open_url(url) return json.loads(res.read()) area = st.selectbox(label="地域", options=list(AREA_ID_DICT.keys()), index=45) area_id = AREA_ID_DICT[area] weather = get_weather(area_id) st.divider() st.header(f'{weather["title"]}') forecast = weather["forecasts"][1] st.markdown(f'**{forecast["dateLabel"]}**') min_temp = forecast["temperature"]["min"]["celsius"] max_temp = forecast["temperature"]["max"]["celsius"] col1, col2 = st.columns(2) col1.metric(label="最高気温", value=f"{max_temp} ℃") col2.metric(label="最低気温", value=f"{min_temp} ℃") st.divider() st.markdown(f'{weather["description"]["bodyText"]}')
なおAREA_ID_DICTは天気予報APIのエリア指定に必要なIDを入れているものです。長いので本記事では一部のみ掲載します。元のデータはこちらのXMLです。
stliteはrequests
やurllib
が使えないようなのでAPI呼び出しのときは少し注意が必要そうです。stliteのリポジトリのlimitationsの節にも次のように書いてあります。
For URL access,
urllib
andrequests
don't work on Pyodide/stlite, so we have to use alternative methods provided by Pyodide, such aspyodide.http.pyfetch()
orpyodide.http.open_url()
.
所感
Streamlitは便利である一方でPythonを動かすバックエンド(サーバー)が必要なのは地味にネックだと思いますので(StreamlitのCommunity Cloudを使って良い状況ならそれで問題ないですが)、stliteはそこのハードルが減っていて非常に良さそうだなと思いました。
またhtmlファイルだけを共有する方法もとれるので、ホスティングするほどじゃないちょっとしたダッシュボードやアプリを共有するのにも一応使えるかなと思いました。
例えば、「社内プロダクト用に作ったAPIがあって、ビジネスサイドの人が『自分の業務フロー中でも使ってみたい』と言ってくれた、でもその人はコードが書けないからGUIのあるアプリを渡したい・・・」みたいな状況で、その人の業務フローに合わせてAPIを呼び出すStreamlitの簡単なアプリを作ってhtmlファイルに埋め込んで渡してあげてPoCする、みたいなことに使えそうです。
なかなかおもしろい技術だと感じました。