最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python使用Flask后端實現(xiàn)解析Excel圖表(附源碼)

 更新時間:2026年01月21日 08:26:09   作者:weixin_46244623  
這篇文章主要介紹了一個完整的 Flask + Vue 3 前端模板 方案,實現(xiàn) 上傳 Excel 文件(不再用鏈接),后端解析 chart1.xml,返回結(jié)構(gòu)化數(shù)據(jù),前端用 ECharts 渲染圖表,感興趣的小伙伴可以了解下

以下是完整的 Flask + Vue 3 前端模板 方案,實現(xiàn) 上傳 Excel 文件(不再用鏈接),后端解析 chart1.xml,返回結(jié)構(gòu)化數(shù)據(jù),前端用 ECharts 渲染圖表。

效果如下

項目結(jié)構(gòu)

project/
├── app.py
├── templates/
│   └── index.html

后端:app.py

import xml.etree.ElementTree as ET
import io
from zipfile import ZipFile, BadZipFile
from flask import Flask, render_template, request, jsonify
from flask_cors import CORS  # 可選,如果有跨域需求

app = Flask(__name__)
CORS(app)  # 允許跨域(同域其實不需要,但留著保險)

def parse_chart_from_bytes(file_data):
    res = {}
    result = {}
    try:
        archive = ZipFile(io.BytesIO(file_data))
        chart_data = archive.read('xl/charts/chart1.xml')

        res['code'] = 200
        res['msg'] = "獲取圖表信息成功"

        tree = ET.parse(io.BytesIO(chart_data))
        root = tree.getroot()

        ns = {
            'c': 'http://schemas.openxmlformats.org/drawingml/2006/chart',
            'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'
        }

        # 圖表類型
        type_mapping = [
            ('pieChart', '餅圖', 'pie'),
            ('lineChart', '折線圖', 'line'),
            ('barChart', '柱形圖', 'bar'),
            ('areaChart', '面積圖', 'line'),
            ('scatterChart', '散點圖', 'scatter'),
            ('radarChart', '雷達(dá)圖', 'radar'),
        ]
        chart_type_cn = '其它'
        echarts_type = 'line'
        is_area = False
        for tag, cn, en in type_mapping:
            if root.find(f'.//c:{tag}', ns) is not None:
                chart_type_cn = cn
                echarts_type = en
                if cn == '面積圖':
                    is_area = True
                break

        result['chart_type_cn'] = chart_type_cn
        result['echarts_type'] = echarts_type
        result['is_area'] = is_area

        # 標(biāo)題
        title_text = ""
        title_elem = root.find('.//c:title/c:tx/c:rich', ns)
        if title_elem is not None:
            for t in title_elem.iterfind('.//a:t', ns):
                if t.text:
                    title_text += t.text
        result['title'] = title_text.strip() or "無標(biāo)題"

        # 系列數(shù)據(jù)
        series_list = []
        ser_elements = root.findall('.//c:ser', ns)

        for idx, ser in enumerate(ser_elements):
            # 系列名稱
            name_elem = ser.find('c:tx/c:v', ns)
            name = name_elem.text if name_elem is not None else f"系列{idx + 1}"

            # 類別(X軸)
            cat_vs = ser.findall('c:cat//c:pt/c:v', ns)
            categories = [v.text for v in cat_vs if v.text is not None]

            # 數(shù)值(Y軸)
            val_vs = ser.findall('c:val//c:pt/c:v', ns)
            values = []
            for v in val_vs:
                if v.text:
                    try:
                        values.append(float(v.text))
                    except ValueError:
                        values.append(v.text)

            series_list.append({
                "name": name,
                "categories": categories,
                "data": values
            })

        # 共享類別軸(如果每個系列都沒有獨立類別)
        if series_list and all(len(s['categories']) == 0 for s in series_list):
            shared_vs = root.findall('.//c:cat//c:pt/c:v', ns)
            shared_cat = [v.text for v in shared_vs if v.text is not None]
            if shared_cat:
                for s in series_list:
                    s['categories'] = shared_cat

        # 餅圖特殊處理
        if chart_type_cn == "餅圖" and series_list:
            for s in series_list:
                pie_data = []
                cats = s['categories'] or [f"項{i+1}" for i in range(len(s['data']))]
                for i, val in enumerate(s['data']):
                    name = cats[i] if i < len(cats) else f"項{i+1}"
                    value = val if isinstance(val, (int, float)) else 0
                    pie_data.append({"name": name, "value": value})
                s['data'] = pie_data

        # 最終類別軸(非餅圖)
        if chart_type_cn != "餅圖" and series_list and series_list[0]['categories']:
            result['categories'] = series_list[0]['categories']
        else:
            result['categories'] = []

        # 系列(只保留 name 和 data)
        result['series'] = [{"name": s["name"], "data": s["data"]} for s in series_list]

        res['data'] = result

    except BadZipFile:
        res['code'] = 404
        res['msg'] = "無效的Excel文件"
    except KeyError:
        res['code'] = 404
        res['msg'] = "未找到圖表信息(無chart1.xml)"
    except Exception as e:
        res['code'] = 500
        res['msg'] = f"解析失敗: {str(e)}"

    return res


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/api/upload_chart', methods=['POST'])
def upload_chart():
    if 'file' not in request.files:
        return jsonify({"code": 400, "msg": "沒有上傳文件"})
    
    file = request.files['file']
    if file.filename == '' or not file.filename.lower().endswith('.xlsx'):
        return jsonify({"code": 400, "msg": "請上傳 .xlsx 文件"})
    
    file_data = file.read()
    return jsonify(parse_chart_from_bytes(file_data))


if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000, debug=True)

前端模板:templates/index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>上傳 Excel → ECharts 渲染</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@3.4.21/dist/vue.global.prod.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
  <style>
    body { font-family: Arial, sans-serif; padding: 20px; background: #f5f5f5; }
    .container { max-width: 800px; margin: auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
    input[type="file"] { padding: 10px; margin-bottom: 15px; }
    button { padding: 12px 24px; font-size: 16px; background: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer; }
    button:hover { background: #66b1ff; }
    button:disabled { background: #a0cfff; cursor: not-allowed; }
    #chart { width: 100%; height: 600px; margin-top: 30px; }
    .msg { margin: 15px 0; font-weight: bold; color: #e6a23c; }
  </style>
</head>
<body>
<div id="app" class="container">
  <h2>上傳 Excel 文件 → ECharts 渲染圖表</h2>
  <input type="file" accept=".xlsx" @change="onFileChange" />
  <button @click="uploadAndRender" :disabled="!selectedFile">上傳并渲染圖表</button>
  <div class="msg">{{ message }}</div>
  <div ref="chartRef" id="chart"></div>
</div>

<script>
const { createApp, ref, onMounted, nextTick } = Vue;

createApp({
  setup() {
    const selectedFile = ref(null);
    const message = ref('請選擇一個包含圖表的 .xlsx 文件');
    const chartRef = ref(null);
    let myChart = null;

    onMounted(() => {
      myChart = echarts.init(chartRef.value);
    });

    const onFileChange = (e) => {
      const file = e.target.files[0];
      if (file && file.name.endsWith('.xlsx')) {
        selectedFile.value = file;
        message.value = `已選擇文件:${file.name}`;
      } else {
        selectedFile.value = null;
        message.value = '請上傳 .xlsx 文件';
      }
    };

    const uploadAndRender = async () => {
      if (!selectedFile.value) return;

      message.value = '正在上傳并解析...';

      const formData = new FormData();
      formData.append('file', selectedFile.value);

      try {
        const response = await fetch('/api/upload_chart', {
          method: 'POST',
          body: formData
        });

        const json = await response.json();

        if (json.code !== 200) {
          message.value = `錯誤:${json.msg}`;
          return;
        }

        const data = json.data;

        const option = {
          title: { text: data.title, left: 'center', textStyle: { fontSize: 18 } },
          tooltip: { trigger: data.echarts_type === 'pie' ? 'item' : 'axis' },
          legend: { data: data.series.map(s => s.name), top: 30 }
        };

        if (data.echarts_type === 'pie') {
          option.series = data.series.map(s => ({
            type: 'pie',
            name: s.name,
            radius: '55%',
            center: ['50%', '60%'],
            data: s.data,
            emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } }
          }));
        } else if (data.echarts_type === 'radar') {
          const maxVal = Math.max(...data.series.flatMap(s => s.data.filter(v => typeof v === 'number')), 100);
          option.radar = {
            indicator: data.categories.map(name => ({ name, max: maxVal * 1.2 }))
          };
          option.series = [{
            type: 'radar',
            data: data.series.map(s => ({ name: s.name, value: s.data }))
          }];
        } else {
          option.xAxis = { type: 'category', data: data.categories };
          option.yAxis = { type: 'value' };
          option.series = data.series.map(s => ({
            type: data.echarts_type,
            name: s.name,
            data: s.data,
            areaStyle: data.is_area ? { opacity: 0.3 } : undefined,
            smooth: data.echarts_type === 'line' ? true : false
          }));
        }

        await nextTick();
        myChart.setOption(option, true);
        message.value = `渲染成功!圖表類型:${data.chart_type_cn}(${data.series.length} 個系列)`;

      } catch (err) {
        message.value = '上傳或解析失敗:' + err.message;
        console.error(err);
      }
    };

    return { selectedFile, message, chartRef, onFileChange, uploadAndRender };
  }
}).mount('#app');
</script>
</body>
</html>

使用方式

1.安裝依賴:

pip install flask flask-cors

2.把上面的文件按結(jié)構(gòu)放好。

3.運行:

python app.py

4.瀏覽器打開 http://127.0.0.1:5000

5.選擇一個包含圖表(chart1)的 .xlsx 文件 → 點擊“上傳并渲染圖表”

功能特點

  • 支持上傳本地 Excel 文件解析(安全,只讀內(nèi)存)。
  • 支持單/多系列圖表。
  • 餅圖、折線、柱形、面積、散點、雷達(dá)圖基本還原。
  • 前端美化了些樣式和交互。

相關(guān)文章

Python使用openpyxl從URL讀取Excel并獲取單元格樣式 

Python解析Excel圖表Chart的信息實戰(zhàn)指南

到此這篇關(guān)于Python使用Flask后端實現(xiàn)解析Excel圖表(附源碼)的文章就介紹到這了,更多相關(guān)Python Flask解析Excel圖表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Django獲取請求IP的正確方法和最佳實踐

    Django獲取請求IP的正確方法和最佳實踐

    這篇文章主要為大家詳細(xì)介紹了Django獲取客戶端IP的正確方法總結(jié)以及常見錯誤和解決方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2026-05-05
  • python實現(xiàn)中文輸出的兩種方法

    python實現(xiàn)中文輸出的兩種方法

    這篇文章主要介紹了python實現(xiàn)中文輸出的兩種方法,實例分析了Python操作中文輸出的技巧,需要的朋友可以參考下
    2015-05-05
  • pytorch中的 .view()函數(shù)的用法介紹

    pytorch中的 .view()函數(shù)的用法介紹

    這篇文章主要介紹了pytorch中的 .view()函數(shù)的用法,主要介紹兩種方法手動調(diào)整size和自動調(diào)整size,下面具體方法分析需要的小伙伴可以參考一下
    2022-03-03
  • 如何使用Python數(shù)據(jù)清洗庫

    如何使用Python數(shù)據(jù)清洗庫

    數(shù)據(jù)清洗是數(shù)據(jù)處理過程中至關(guān)重要的一部分,本文主要介紹了如何使用Python數(shù)據(jù)清洗庫,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • python使用requests庫提交multipart/form-data請求的方法詳解

    python使用requests庫提交multipart/form-data請求的方法詳解

    multipart/form-data的基礎(chǔ)是post請求,即基于post請求來實現(xiàn)的 ,下面這篇文章主要給大家介紹了關(guān)于python使用requests庫提交multipart/form-data請求的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Python基礎(chǔ)教程之增加和去除數(shù)字的千位分隔符

    Python基礎(chǔ)教程之增加和去除數(shù)字的千位分隔符

    千位分隔符其實就是數(shù)字中的逗號,下面這篇文章主要給大家介紹了關(guān)于Python基礎(chǔ)教程之增加和去除數(shù)字的千位分隔符,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • python獲取當(dāng)前時間對應(yīng)unix時間戳的方法

    python獲取當(dāng)前時間對應(yīng)unix時間戳的方法

    這篇文章主要介紹了python獲取當(dāng)前時間對應(yīng)unix時間戳的方法,涉及Python時間操作的相關(guān)技巧,非常簡單實用,需要的朋友可以參考下
    2015-05-05
  • 詳解python多線程、鎖、event事件機制的簡單使用

    詳解python多線程、鎖、event事件機制的簡單使用

    這篇文章主要介紹了詳解python多線程、鎖、event事件機制的簡單使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • python實現(xiàn)用戶管理系統(tǒng)

    python實現(xiàn)用戶管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了python實現(xiàn)用戶管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Python進(jìn)度條可視化之監(jiān)測程序運行速度

    Python進(jìn)度條可視化之監(jiān)測程序運行速度

    Tqdm是一個快速,可擴(kuò)展的Python進(jìn)度條,可以在Python長循環(huán)中添加一個進(jìn)度提示信息,用戶只需要封裝任意的迭代器即可。本文就主要介紹了通過進(jìn)度條檢測程序運行速度,感興趣的同學(xué)可以學(xué)習(xí)一下
    2021-12-12

最新評論

卢湾区| 图木舒克市| 光山县| 大方县| 叙永县| 台前县| 涡阳县| 屯门区| 大足县| 蓬莱市| 道真| 桐城市| 沁水县| 革吉县| 博爱县| 牙克石市| 日喀则市| 平度市| 天气| 轮台县| 兰溪市| 拉孜县| 尚志市| 西林县| 布尔津县| 城固县| 和林格尔县| 专栏| 巴彦县| 冷水江市| 伊吾县| 灵丘县| 安远县| 新野县| 海南省| 昭通市| 聂拉木县| 平安县| 乐都县| 当雄县| 陇西县|