ツイーティ・ザ・フォックスと申します。
Qiita初投稿です。新参者なのでマサカリ歓迎です。
Zennにもエラー対処法を中心に投稿を行っています。
https://zenn.dev/tweeteafox300?tab=scraps
記事概要と検証環境
PythonでWebアプリを作成できるライブラリのGradioで、ディレクトリに配置したhtmlファイルを、iframe(srcdoc)とgradio.HTML
を使って表示させる方法を、サンプルによって説明します。
Hugging Faceのフォーラムで提案されていた別の方法:★参考にした記事(本記事とは別の方法)に、難しい部分や原因不明のエラー発生があったので、それらよりもシンプルな方法を考えてみました。
検証環境
・pythonは3.11
、Gradioは4.37.2
です。
・ローカル環境(Win10のPowershell)に起動したローカルサーバーで確認。
・本番環境(Hugging Face等)にデプロイした時の挙動は確認していません。
[tool.poetry.dependencies]
python = "^3.11"
gradio = "^4.37.2"
[本題]:Gradioでhtmlファイルを読み込み、iframe(srcdoc)とgradio.HTMLで表示
0:ディレクトリ構成
Pythonのプログラムとhtmlファイルのみです。
.
├── app.py
└── html/
└── test.html
1:表示させるhtmlファイルを準備
GradioのUI上に表示させたいhtmlファイルを./html/test.html
に作成します。
今回準備した簡単なhtmlファイル
--------------折りたたみ----------------
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
background-color:aquamarine;
}
</style>
</head>
<body>
<h1>html埋め込みテスト</h1>
<h2>テスト1</h2>
<h3>テスト2</h3>
<textarea id="testtextarea">あああああ</textarea>
<button id="testbutton">JavaScriptテストボタン</button>
<script>
// ボタンを押すとテキストエリアの中身を変えるだけ
const tarea = document.getElementById("testtextarea");
document.getElementById("testbutton")
.addEventListener("click", function () {
tarea.value = "ボタンが押されるとこの文字列を表示\n\nこれが表示できている\n=JavaScriptの動作に成功";
});
</script>
</body>
</html>
2:iframe(srcdoc)とgradio.HTML
を使ってGradio上で表示!
- ディレクトリに配置したhtmlを
open
で読み込み、文字列として取得します。
- Python標準モジュールの
HTML.escape
を使用して、html文字列内にあるhtml特殊文字をエスケープします。
https://docs.python.org/ja/3/library/html.html
- f文字列を使って「iframeタグのsrcdocプロパティに、エスケープ済みのhtml文字列を入れ込んだ文字列」を作成します。srcdocプロパティにエスケープしていないhtml文字列(特に
"
)を入れ込んでしまうと、iframeタグが機能しなくなってしまうので注意です。
https://developer.mozilla.org/ja/docs/Web/API/HTMLIFrameElement/srcdoc
- 作成したiframeタグ文字列を
gradio.HTML
のコンポーネントに入力すると、htmlを表示することができます!
https://www.gradio.app/docs/gradio/html
import gradio as gr
# gradionのHTMLと重複してエラー出た?ので別名に
import html as html_lib
# 表示させるhtmlファイルのパス
html_file_pass = "./html/test.html"
def load_html():
# htmlファイルを文字列として読み込む
html_string = open(html_file_pass, "r", encoding="UTF-8").read()
# python標準のhtmlモジュールでHTML特殊文字をエスケープする
# iframeのsrcdoc内にいれる関係で、ダブルクォート(")含めエスケープが必要
# https://docs.python.org/ja/3/library/html.html
html_escaped = html_lib.escape(html_string, quote=True)
# iframeのsrcdoc内に読み込んだhtmlの文字列を入れる
# https://developer.mozilla.org/ja/docs/Web/API/HTMLIFrameElement/srcdoc
iframe = (
f"""<iframe srcdoc="{html_escaped}" width="100%" height="300px"></iframe>"""
)
return iframe
with gr.Blocks() as html_test:
gr.Markdown("# エメラルド色の部分がiframeで埋め込んだhtml")
view_btn = gr.Button("./html/test.htmlを読み込み表示", variant="primary")
# iframeのタグをgr.HTMLに入れて表示させる
view_html = gr.HTML(label="HTML preview", show_label=True)
view_btn.click(fn=load_html, outputs=view_html)
html_test.launch()
3:実際に表示される画面
上記のプログラムをgradio app.py
で実行、ローカルサーバーを起動してアクセスすると、このようになります。CSSでの色変更(エメラルド色)と、JavaScriptの動作(ボタンのイベントリスナー)が効いていることを確認できます。
★参考にした記事(本記事とは別の方法)
Gradio上でhtmlファイルを生成し、それのファイルパスをiframeのsrcでfile={ファイルパス}
と指定して表示
Hugging Faceのフォーラム「How to serve an HTML file?」の8番目の投稿で共有されている方法
https://discuss.huggingface.co/t/how-to-serve-an-html-file/33921/8
- この方法を本記事のサンプルと同じようなやつで試した所、iframeの部分で
{“detail”:“File not allowed: /html/test.html.”}
というエラーが出て失敗してしまいました。hugging faceのディスカッション「How to access tmp files of a space?」で同様のエラーが報告されているのですが、2024年7月27日時点では、原因不明かつ解決策が出ていないようでした。
https://discuss.huggingface.co/t/how-to-access-tmp-files-of-a-space/59257
FastAPIアプリにGradioアプリをマウント、Gradio上でhtmlを生成し、FastAPIで静的ページとして公開。Gradioでそれのパスをiframeのsrcに指定して表示
Hugging Faceのフォーラム「How to serve an HTML file?」の2番目の投稿で共有されている方法
https://discuss.huggingface.co/t/how-to-serve-an-html-file/33921/2
- Gradioに加えて、FastAPIも同時に使用する方法なので、ハードルがちょっと高いかもです。
https://fastapi.tiangolo.com/ja/
★参考にした記事(応用例)
FastAPI+Gradioで、クエリの結果をインタラクティブな表として閲覧できる、DuckDBのSQLエディタを作成した事例
一つ上にあるような「Gradio上でhtmlを生成して、FastAPIで静的ページとして公開」というアプローチを使うと、かなり複雑なことができるみたいです。
Live DuckDB editor on Gradio
https://medium.com/@liquidc/live-duckdb-editor-on-gradio-533addd0666c
duckdb-fastapi-gradio
https://huggingface.co/spaces/liquidcarbon/duckdb-fastapi-gradio