使用python-flask和echarts完成數據可視化


使用python-flask和echarts完成數據可視化

一、工具介紹

flask是一個Python實現的Web開發微框架,類似的還有django/dash等。這篇文章是一個講述如何用它實現數據可視化的詳細教程。

echarts是一個純JavaScript數據可視化圖標庫,兼容絕大部分的瀏覽器。

​ 本文利用Python Flask框架與echarts相結合,展示了一個從建立數據庫,到Python封裝數據庫信息為json格式數據,前端接受json格式數據並執行響應,最終展示數據的到頁面的一個完整的流程。以下是項目的文件結構:

項目結構
Goods文件夾
|-static
||-js
|||-jquery.min.js
|||-echarts.js
||-css
|||-style.css
|-templates     html在該文件夾下
||-index.html
|-app.py
二、代碼講解
1. sqlalchemy

​ 使用 SQLAlchemy 的首要原因是,它將你的代碼從底層數據庫及其相關的 SQL 特性中抽象出來。下面是sqlalchemy的數據庫鏈接與查詢(注意需要提前構建好基類才能進行查詢)

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, INTEGER, String
from sqlalchemy.orm import sessionmaker

HOST_NAME = 'localhost'     # 數據庫所在服務器ip,因為我是本地數據庫所以這里是127.0.0.1
HOST_PORT = '3306'              # 數據庫端口
DATABASE_NAME = 'flasktest'   # 數據庫名
USER_NAME = 'root'          # 鏈接數據的用戶名
PWD = 'root'              # 鏈接數據庫的密碼
DB_URI = 'mysql+pymysql://{0}:{1}@{2}:{3}/{4}?charset=utf8'.format(USER_NAME,PWD,HOST_NAME,HOST_PORT,DATABASE_NAME)
# 創建數據庫連接
engine = create_engine(DB_URI)
# 操作數據庫基類
Base = declarative_base(engine)

class UserModule(Base):
    """
    創建一個用戶的數據模型
    """
    __tablename__ = 'flasktest'
    id = Column(INTEGER, primary_key=True, autoincrement=True, comment='用戶id')
    Goods_name = Column(String(30), nullable=False, unique=True, comment='商品名')
    Goods_sales_volume = Column(INTEGER, nullable=False, comment='產量')
    Goods_inventory = Column(INTEGER, nullable=False, comment='銷量')

    def __repr__(self):
        return 'User(id={id}, Goods_name={Goods_name}, Goods_sales_volume={Goods_sales_volume}, money={Goods_inventory})'.format(
            id=self.id, Goods_name=self.Goods_name, Goods_sales_volume=self.Goods_sales_volume, Goods_inventory=self.Goods_inventory)

def get_db():
    Session = sessionmaker(bind=engine)
    session = Session()  # 實例化了一個會話(或叫事務),之后的所有操作都是基於這個對象的
    inventQuery = session.query(UserModule.Goods_inventory).all()
    scores = [c[0] for c in inventQuery]
    salesQuery = session.query(UserModule.Goods_sales_volume).all()
    money = [c[0] for c in salesQuery]
    nameQuery = session.query(UserModule.Goods_name).all()
    names = [c[0] for c in nameQuery]
    return names, scores, money
2. flask框架

render_template返回對應的html鏈接。

​ 兩個viewdata路由用於分別對兩個表格傳輸數據,完成后端對前端的數據回傳

jsonify將dict數據打包成json格式,方便前端讀取。

from flask import Flask, request
from flask import render_template
from flask import jsonify

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

# 前端會到這個url請求數據,通過ajax動態刷新數
@app.route("/viewdata", methods=["GET"]) 
def viewdata():
    if request.method == "GET":
        names, scores, money = get_db()
    return jsonify(name = [x for x in names],
                   score = [x for x in scores],
                   money = [x for x in money])

@app.route("/viewdata2", methods=["GET"]) # url匹配ajax
def viewdata2():
    if request.method == "GET":
        names, scores, money = get_db()
    return jsonify(name = [x for x in names],
                   score = [x for x in money],
                   money = [x for x in scores])

if __name__ == '__main__':
    app.run(debug=True, port=5000) #port:端口,默認端口為5000,訪問地址為localhost:5000/
3. index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>ECharts3 Ajax</title>
    <script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
    <script src="{{ url_for('static', filename='echarts.js') }}"></script>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" >
</head>

<body>
    <!--為ECharts准備一個具備大小(寬高)的Dom-->
    <div id="main" style="height:500px;border:1px solid #ccc;padding:10px;"></div>
    <div id="example" style="height:500px;border:1px solid #ccc;padding:10px;"></div>
    <div id="view"></div>

    <script type="text/javascript">
        var myChart = echarts.init(document.getElementById('main'));
        var exChart = echarts.init(document.getElementById('example'));
        // 顯示標題,圖例和空的坐標軸
        myChart.setOption({
            title: {
                text: '異步數據加載示例'
            },
            tooltip: {},
            legend: {
                data:['score','money']
            },
            xAxis: {
                data: []
            },
            yAxis: {},
            series: [{
                name: 'score',
                type: 'line',
                data: []
            },{
                name: 'money',
                type: 'bar',
                data: []
            }]
        });

        myChart.showLoading(); // 顯示加載動畫

        // 從url請求數據,異步加載
        $.get('/viewdata').done(function (data) {
            myChart.hideLoading(); // 隱藏加載動畫
            // 填入數據
            myChart.setOption({
                xAxis: {
                    data: data.name
                },
                series: [{
                    name: 'score', // 根據名字對應到相應的系列
                    data: data.score.map(parseFloat) // 轉化為數字(注意map)
                },{
                    name: 'money',
                    data: data.money.map(parseFloat)
                }]
            });
        });

        // 第二個表格
        exChart.setOption({
            title: {
                text: '異步數據加載示例'
            },
            tooltip: {},
            legend: {
                data:['score','money']
            },
            xAxis: {
                data: []
            },
            yAxis: {},
            series: [{
                name: 'score',
                type: 'line',
                data: []
            },{
                name: 'money',
                type: 'bar',
                data: []
            }]
        });
        exChart.showLoading(); // 顯示加載動畫
        // 異步加載數據
        $.get('/viewdata2').done(function (data) {
            exChart.hideLoading(); // 隱藏加載動畫
            // 填入數據
            exChart.setOption({
                xAxis: {
                    data: data.name
                },
                series: [{
                    name: 'score', // 根據名字對應到相應的系列
                    data: data.score.map(parseFloat) // 轉化為數字(注意map)
                },{
                    name: 'money',
                    data: data.money.map(parseFloat)
                }]
            });
        });
    </script>
</body>
</html>

​ 代碼有點多,選重點部分講。第一部分如下,主要是讀取了div容器,然后設置好不需要動態加載的圖表的橫縱坐標軸等信息。

 <script type="text/javascript">
        var myChart = echarts.init(document.getElementById('main'));
        // 顯示標題,圖例和空的坐標軸
        myChart.setOption({
            title: {
                text: '異步數據加載示例'
            },
            tooltip: {},
            legend: {
                data:['score','money']
            },
            xAxis: {
                data: []
            },
            yAxis: {},
            series: [{
                name: 'score',
                type: 'line',
                data: []
            },{
                name: 'money',
                type: 'bar',
                data: []
            }]
        });

​ 第二部分是重點,主要負責通過ajax技術動態的接收數據庫回傳的data,實時地加載到前端的echarts組件中。$.get('/viewdata').done(function (data)) 表示從url請求數據,然后執行函數。其中$.get(url,data,success(response,status,xhr),dataType)是簡寫的ajax函數,等價於如下代碼。

$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});
        myChart.showLoading(); // 顯示加載動畫
        // 從url請求數據,異步加載
        $.get('/viewdata').done(function (data) {
            myChart.hideLoading(); // 隱藏加載動畫
            // 填入數據
            myChart.setOption({
                xAxis: {
                    data: data.name
                },
                series: [{
                    name: 'score', // 根據名字對應到相應的系列
                    data: data.score.map(parseFloat) // 轉化為數字(注意map)
                },{
                    name: 'money',
                    data: data.money.map(parseFloat)
                }]
            });
        });

因此以上內容也可以改寫如下:

   function getData() {
            $.ajax({
                cache: false,
                type: "GET",
                url: "/viewdata", //把表單數據發送到/viewdata
                data: {}, // 發送的數據,這里是前端接收數據,所以是空
                dataType : "json",  //返回數據形式為json
                async: false,
                error: function(request) {
                    alert("發送請求失敗!");
                },
                success: function(result) {
                    myChart.hideLoading(); // 隱藏加載動畫
                    // console.log(result);
                    myChart.setOption({
                        xAxis: {
                            data: result.name
                        },
                        series: [{
                            name: 'score', // 根據名字對應到相應的系列
                            data: result.score.map(parseFloat) // 轉化為數字(注意map)
                        },{
                            name: 'money',
                            data: result.money.map(parseFloat)
                        }]
                    });
                }
            });
        };
        //頁面加載完畢,發送ajax請求
        $(document).ready(function () {
            getData();
        });
4. 前后端交互

​ 講到這里,筆者覺得有必要記錄以下前后端交互的操作方法。Web應用基於ajax進行前后端數據交互,一般利用Get或者Post方式來實現。比較流行的做法是前端提交表單數據,后端處理完畢后返回Json數據到前端進行顯示。以下是前端發送數據到后端的流程代碼。

4.1 前端發送到后端

get請求

​ 前端:

<script src="{{url_for('static',filename='js/jquery.js')}}"></script>
<script>
    var data={
        'name':'kikay',
        'age':18
    }
    $.ajax({
        type:'GET',
        url:'{{url_for("test_get")}}',
        data:data,
        dataType:'json',//希望服務器返回json格式的數據
        success:function(data){
            alert(JSON.stringify(data));
            alert(data['test'])
        }
    });
</script>

​ 后端:request.args.get獲取get請求數據

@test.route('/test_get/',methods=['POST','GET'])
def test_get():
    #獲取Get數據
    name=request.args.get('name')
    age=int(request.args.get('age'))
    #返回
    if name=='kikay' and age==18:
        return jsonify({'result':'ok'})
    else:
        return jsonify({'result':'error'})

post請求

​ 前端:

<script src="{{url_for('static',filename='js/jquery.js')}}"></script>
<script>
    var data={
        'name':'kikay',
        'age':18
    }
    $.ajax({
        type:'POST',
        url:'{{url_for("test_post")}}',
        data:data,
        dataType:'json',//希望服務器返回json格式的數據
        success:function(data){
            alert(JSON.stringify(data));
        }
    });
</script>

​ 后端:request.form.get獲取get請求數據

@test.route('/test_post/',methods=['POST','GET'])
def test_post():
    #獲取POST數據
    name=request.form.get('name')
    age=int(request.form.get('age'))
    #返回
    if name=='kikay' and age==18:
        return jsonify({'result':'ok'})
    else:
        return jsonify({'result':'error'})

4.2 后端發送給前端

​ 后端發送數據給前端主要通過return回dic/json。

@app.route("/")
def index():
	dic={}
	dic['name']='xiaomin'
	dic['num']=1
	return render_template('index.html',data=dic)

​ 前端通過接收數據只需要把ajax相關代碼中的data設置為空。

    $.ajax({
        type:'GET',
        url:'{{url_for("test_get")}}',
        data:{}, //不發送data
        dataType:'json',//希望服務器返回json格式的數據
        success:function(data){ //從這里接收到data
            alert(JSON.stringify(data));
            alert(data['test'])
        }
5. CSS樣式設置

​ 最后就是調整兩個div容器的位置和樣式,插入到html中。

三、結果

​ 數據僅供顯示,無任何意義。運行成功后界面如下:image-20210228012018542

四、展望
  1. 文件分層:當項目較大的時候,可以把數據的查詢功能(sql)放在單獨的腳本里utils.py,在app.py里只負責調用接口和創建路由。同樣當js樣式較多時候,可以把ajax請求放在controller.js中中轉,在其他js文件里渲染靜態樣式。

    1. 還可以通過china.js/lefatlet.js等第三方庫添加地圖進行顯示。
    2. 前后端交互的原理相當重要,前后端數據的發送和接收方法值得注意。
    3. 后續還需部署服務,以實現在別的電腦上進行訪問。
    4. 今天做的只是一個簡單樣例,后續可以實現https://blog.csdn.net/hxxjxw/article/details/105336981中的疫情可視化系統完成完整系統的設計。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM