使用PyScript在HTML中嵌入Matplotlib财富分布图遇加载问题求助
使用PyScript集成Python财富分布图表到网页(解决本地CSV加载与无限加载问题)
核心问题定位
你遇到的页面无限加载和无内容输出,大概率是因为PyScript运行在浏览器WebAssembly环境中,无法直接读取本地文件系统路径,同时Matplotlib的默认渲染方式不兼容浏览器DOM,导致代码阻塞或无输出。以下是针对性的解决方案:
1. 修正CSV文件加载方式
PyScript不能像本地Python那样用pd.read_csv("local_path.csv")读取文件,必须通过HTTP请求加载静态资源。把你的CSV文件放在网页同目录下(比如wealth_data.csv),用pyfetch异步获取内容:
import pandas as pd from pyodide.http import pyfetch import asyncio from io import StringIO async def load_data(): response = await pyfetch("wealth_data.csv") if response.status == 200: csv_content = await response.text() return pd.read_csv(StringIO(csv_content)) else: raise Exception(f"数据加载失败: {response.status}") # 异步加载数据,避免阻塞页面 df = asyncio.run(load_data())
2. 适配Matplotlib浏览器渲染
Matplotlib默认的后端无法直接在浏览器中输出,需要把图表转为图片插入DOM:
import matplotlib.pyplot as plt from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas import base64 from js import document def plot_wealth_distribution(country): # 替换为你的原有图表生成逻辑 country_data = df[df['country'] == country] fig, ax = plt.subplots(figsize=(8, 6)) ax.bar(country_data['percentile'], country_data['wealth']) ax.set_title(f"{country} 财富百分位分布") ax.set_xlabel("财富百分位") ax.set_ylabel("财富值 (USD)") # 将图表转为图片并插入网页 canvas = FigureCanvas(fig) canvas.draw() img_data = canvas.tostring_rgb() width, height = fig.get_size_inches() * fig.dpi img = document.createElement("img") img.src = f"data:image/png;base64,{base64.b64encode(img_data).decode()}" img.width = width img.height = height # 清空旧图表,添加新图表 chart_container = document.getElementById("chart-container") while chart_container.firstChild: chart_container.removeChild(chart_container.firstChild) chart_container.appendChild(img) plt.close(fig) # 释放资源,避免内存泄漏
3. 添加用户交互(国家选择功能)
在HTML中添加交互元素,并用PyScript绑定事件:
HTML部分
<div> <label for="country-select">选择国家:</label> <select id="country-select"></select> <button id="plot-btn">生成图表</button> </div> <div id="chart-container"></div>
PyScript部分
# 动态填充国家选项 countries = df['country'].unique() select = document.getElementById("country-select") for country in countries: option = document.createElement("option") option.value = country option.textContent = country select.appendChild(option) # 绑定按钮点击事件 def on_plot_click(event): selected_country = document.getElementById("country-select").value plot_wealth_distribution(selected_country) document.getElementById("plot-btn").addEventListener("click", on_plot_click)
4. 完整HTML框架示例
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>国家财富分布对比工具</title> <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" /> <script defer src="https://pyscript.net/latest/pyscript.js"></script> </head> <body> <h1>国家财富百分位分布对比</h1> <div> <label for="country-select">选择国家:</label> <select id="country-select"></select> <button id="plot-btn">生成图表</button> </div> <div id="chart-container"></div> <py-env> - pandas - matplotlib </py-env> <py-script> # 这里放入上述所有Python代码 import pandas as pd from pyodide.http import pyfetch import asyncio from io import StringIO import matplotlib.pyplot as plt from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas import base64 from js import document async def load_data(): response = await pyfetch("wealth_data.csv") if response.status == 200: csv_content = await response.text() return pd.read_csv(StringIO(csv_content)) else: raise Exception(f"数据加载失败: {response.status}") df = asyncio.run(load_data()) def plot_wealth_distribution(country): country_data = df[df['country'] == country] fig, ax = plt.subplots(figsize=(8, 6)) ax.bar(country_data['percentile'], country_data['wealth']) ax.set_title(f"{country} 财富百分位分布") ax.set_xlabel("财富百分位") ax.set_ylabel("财富值 (USD)") canvas = FigureCanvas(fig) canvas.draw() img_data = canvas.tostring_rgb() width, height = fig.get_size_inches() * fig.dpi img = document.createElement("img") img.src = f"data:image/png;base64,{base64.b64encode(img_data).decode()}" img.width = width img.height = height chart_container = document.getElementById("chart-container") while chart_container.firstChild: chart_container.removeChild(chart_container.firstChild) chart_container.appendChild(img) plt.close(fig) countries = df['country'].unique() select = document.getElementById("country-select") for country in countries: option = document.createElement("option") option.value = country option.textContent = country select.appendChild(option) def on_plot_click(event): selected_country = document.getElementById("country-select").value plot_wealth_distribution(selected_country) document.getElementById("plot-btn").addEventListener("click", on_plot_click) </py-script> </body> </html>
关键注意事项
- 确保CSV文件和HTML文件放在同一目录,或在
pyfetch中使用正确的相对路径 <py-env>必须声明所有依赖包(比如pandas、matplotlib),否则会导致加载失败- 所有异步操作必须用
async/await处理,避免阻塞页面线程
内容的提问来源于stack exchange,提问作者Leo Wu
相关产品推荐
相关产品推荐

