StreamlitでOGPタグを設定する方法

StreamlitでOGPタグを設定する方法


Streamlitはデータ分析を元に可視化をするためのフレームワークとして人気があります。最近はデータ分析だけでなく、アプリケーションのフロントエンドとしても可能性があると思って私も利用しています。

Streamlitでアプリを一般に公開するとき、SNSでシェアするとそっけない印象になります。これはSNSなどでリンクの情報を集めるときにOGPタグを読み込む際に、Streamlitでは設定する手段がないためです。

公式ではStreamlit Cloud側ではある程度設定されるようです。ただ別の環境でデプロイしている場合は方法(インターフェイス)は特に提供されていません。

st.set_page_configではst.set_page_configではタイトルやfaviconなどは設定できますが、OGPタグを完全に設定する手段はありません。これをどうにかします。

ハック的な方法:パッケージのファイルを修正する

コミュニティでのディスカッションを探ると、Streamlitのフロントエンド側(React)で使うテンプレート的なhtmlファイルがあり、これにOGPタグを直接埋め込むという方法です。かなりハック的な方法です。

方法はデプロイする前にパッケージ内にあるstatic/index.htmlを編集するだけです。が、一度行っても、アップデートで元に戻ってしまうため恒久的ではありません。

<!-- ソースから抜粋 -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <link rel="shortcut icon" href="/favicon.png" />
    <link
      rel="preload"
      href="./static/media/SourceSansPro-Regular.DZLUzqI4.woff2"
      as="font"
      type="font/woff2"
      crossorigin
    />
    <link
      rel="preload"
      href="./static/media/SourceSansPro-SemiBold.sKQIyTMz.woff2"
      as="font"
      type="font/woff2"
      crossorigin
    />
    <link
      rel="preload"
      href="./static/media/SourceSansPro-Bold.-6c9oR8J.woff2"
      as="font"
      type="font/woff2"
      crossorigin
    />

    <!-- このあたりにmetaタグを埋め込めばいい -->

    <title>Streamlit</title>

    <!-- initialize window.prerenderReady to false and then set to true in React app when app is ready for indexing -->
    <script>
      window.prerenderReady = false
    </script>
  </head>
<!-- 以下省略-->
<!-- ref: [streamlit/frontend/app/index.html at de36f41d99e9c1477045113e603fa6a246dfe3ea · streamlit/streamlit](https://github.com/streamlit/streamlit/blob/de36f41d99e9c1477045113e603fa6a246dfe3ea/frontend/app/index.html#L17-L54) -->

最近はDockerなどのコンテナを使ったデプロイが出来るので、Dockerfile内で追加するスクリプトを用意すると良いです。参考元のスクリプトで十分機能します。自分が使ってるスクリプトはこういう感じです。

# inject_meta_ogp.py
import shutil
from pathlib import Path

from bs4 import BeautifulSoup

# パスは環境によって異なるので、適宜変更してください。pathはdockerでの動作を想定
_python_version = "3.12"
sitepackage_path = Path(f"/usr/local/lib/python{_python_version}/site-packages")
streamlit_path = sitepackage_path / Path("streamlit/static/index.html")

# ここでOGPタグを設定する
SERVICE_NAME = "3Dプリントアイコン"
SERVICE_URL = "https://3dprinticon.com/"
SHARE_TEXT = "3Dプリントアイコンは、3Dプリンター用のアイコンやプレートを提供するサービスです。"
OGPKEYWORDS = "3Dプリント, 3Dプリンター, アイコン, プレート"  # カンマ区切りで
OGIMAGE_URL = f"{SERVICE_URL}app/static/images/ogimage.jpg"

# 一応バックアップ
shutil.copy2(streamlit_path, streamlit_path.with_suffix(streamlit_path.suffix + ".bak"))

# stremalitのパッケージ内 static/index.htmlを読み込む
with open(streamlit_path, "r") as file:
    html_content = file.read()

# BeautifulSoupでHTMLを解析
soup = BeautifulSoup(html_content, "html.parser")

# OGPタグを追加するための情報
meta_tags = [
    # General SEO
    {
        "name": "description",
        "content": SHARE_TEXT,
    },
    # Open Graph / Facebook
    {"property": "og:type", "content": "website"},
    {"property": "og:url", "content": SERVICE_URL},
    {"property": "og:title", "content": SERVICE_NAME},
    {
        "property": "og:description",
        "content": SHARE_TEXT,
    },
    {
        "property": "og:image",
        "content": OGIMAGE_URL,
    },
    # Twitter
    {"name": "twitter:card", "content": SERVICE_NAME},
    {"name": "twitter:url", "content": SERVICE_URL},
    {"name": "twitter:title", "content": SERVICE_NAME},
    {
        "name": "twitter:description",
        "content": SHARE_TEXT,
    },
    {
        "name": "twitter:image",
        "content": OGIMAGE_URL,
    },
    # LinkedIn
    {"name": "linkedin:title", "content": SERVICE_NAME},
    {
        "name": "linkedin:description",
        "content": SHARE_TEXT,
    },
    {
        "name": "linkedin:image",
        "content": OGIMAGE_URL,
    },
    # Additional tags
    {"name": "author", "content": "kata-studio"},
    {
        "name": "keywords",
        "content": OGPKEYWORDS,
    },
]

# metaタグを追加
for tag in meta_tags:
    new_tag = soup.new_tag("meta")
    for key, value in tag.items():
        new_tag[key] = value
    soup.head.append(new_tag)

# 新しいHTMLをファイルに書き込む
with open(streamlit_path, "w") as file:
    file.write(str(soup))
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY add_meta_tags.py .
RUN python add_meta_tags.py

COPY . .

EXPOSE 8501
CMD ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]

注意点としては、マルチページ構成をしているときに、OGPタグ自体はここで追加したもののみが使われるので、ページ単位でOGPタグを設定するのは難しいです。

これ以外の方法は私は試していませんが、リバースプロキシでhtmlのタグを置き換える手段はあるようです(とChatGPTやGeminiなど生成AIに教わりました)。またSNSのOGPタグを見に行くクローラーのみに専用のOGPタグを見せるようなこともできそうです(ダイナミックレンダリングとも呼ばれます、と生成AIに教えてもらった)

まとめ

Streamlitはデータ分析でよく使われていますが、Pythonを活用できる都合で様々なアプリのフロントエンドとしても使えると思います。ただそもそもデータ分析の分野で特定の共有範囲で利用されるシチュエーションが想定されていたようなので、アプリ公開のための機能は、まだまだ弱いと感じています。

最近はst.loginのようなStreamlitで作ったアプリを使うユーザーの認証機能が欲しい需要がカバーされつつあるので、何かしらの要望を出すと良いのかもしれません。

(...という話がすでに GitHub Issue で追加されているので、ぜひリアクションしてもらえると嬉しいです。

Social sharing meta tags outside of Community Cloud · Issue #6567 · streamlit/streamlit


Streamlit入門 Pythonで学ぶデータ可視化&アプリ開発ガイド 技術の泉シリーズ
B0DP6SSQ67

ASIN : B0DP6SSQ67
Amazonで詳しく見る
Powered by Amazon Quick Affiliate (JP)

こちらはAmazonアソシエイトプログラム参加リンクです