Flask+Echarts+sqlite搭建股票實時行情監控

從小白出發帶你領略Python爬蟲之美,關注微信公眾號:機智出品(jizhjchupin),免費獲取源代碼及實戰教程

一、系統環境:

Windows7+Python3.6+Echart3

二、結果展示:

股票實時行情.gif

三、實現過程:

PART ONE:tushare數據獲取sqlite存儲[monitor.py]

step 1:需要的庫
from sqlalchemy import create_engine
import tushare as ts
import time
import pandas as pd
step2:獲取行情并存儲

這里我們使用的是Tushare隔3S獲取一次股票實時行情數據,并存儲至sqlite。

def stoc(i):
    df = ts.get_realtime_quotes('600031') 
    data=[i]
    s1 = pd.Series(data, index=range(0,1,1))
    df['id'] = s1
    engine = create_engine('sqlite:///test.db', convert_unicode=True)
    #追加數據到現有表
    df.to_sql('tick_data',engine,if_exists='append')

i=0
while True:
    stoc(i)
    time.sleep(3)
    i=i+1
    print('insert %s datas'%i)

PART TWO:路由器[app.py]

step 1:需要的庫
import sqlite3
from flask import *
import os
step 2:設置
app = Flask(__name__)
app.config['USERNAME']='finance'#賬號密碼
app.config['PASSWORD']='finance'
app.secret_key=os.urandom(24)
step 3:URL
@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in')
            return redirect(url_for('index'))
    return render_template('login.html', error=error)

@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('You were logged out')
    return redirect(url_for('login'))

def get_db():
    db = sqlite3.connect('test.db')
    db.row_factory = sqlite3.Row
    return db

def query_db(query, one=False):#, args=()
    db = get_db()
    cur = db.execute(query)#, args
    db.commit()
    rv = cur.fetchall()
    db.close()
    return (rv[0] if rv else None) if one else rv

@app.route("/", methods=["GET"])
def index():
    if session.get('logged_in'):
        return render_template("index.html")
    else:
        return redirect(url_for('login'))
    
@app.route("/finance", methods=["POST"])
def finance():
    if request.method == "POST":
        res = query_db("SELECT * FROM tick_data ") 
        print(int(request.form['id']))
    return jsonify(time = [x[32] for x in res],
                   price = [x[4] for x in res]) # 返回json格式

if __name__ == "__main__":
    app.run(debug=True)

PART THREE:頁面[index.html]

<!DOCTYPE html>
<html style="height: 100%" lang="en">
<head>
    <meta charset="utf-8">
    <title>My Finance</title>
    <script src="{{ url_for('static', filename='jquery-3.1.1.js') }}"></script>
    <script src="{{ url_for('static', filename='echarts.js') }}"></script>
    <!-- 引入 vintage 主題 -->
    <script src="{{ url_for('static', filename='dark.js') }}"></script>
</head>
<body style="height: 100%; margin: 0">
    <div id="main" style="height: 100%"></div>
    <script type="text/javascript">
    var myChart = echarts.init(document.getElementById('main'), 'dark');
    myChart.setOption({
        title: {
            top: 5,
            left: 'center',
            text: 'My Finance',
            textStyle: {
            color: '#00FFFF',
            fontSize: 18
            }
        },
        tooltip: {
            trigger: 'axis',
            axisPointer: {
            animation: false,
            type: 'cross',
            lineStyle: {
                color: '#376df4',
                width: 2,
                opacity: 1
                }
            }
        },
        legend: {
            top: 30,
            left: 'center',
            data:['price']
        },
        toolbox: {
        show : true,
        orient : 'vertical',
        top : '35%',
        right : 45,
        feature : {
            mark : {show: true},
            dataView : {show: true, readOnly: false},
            magicType : {show: true, type: ['line', 'bar']},
            restore : {show: true},
            saveAsImage : {show: true}
            }
        },
        xAxis: {
            data: [],
            axisLine: { lineStyle: { color: '#8392A5' } }
        },
        yAxis: {
        //maxInterval: 0.1
        //interval:0.1,
        min:'dataMin',
        axisLine: { lineStyle: { color: '#8392A5' } }
        //max:'dataMax'
        //splitNumber:5
        },
        dataZoom: [
            {
                "show": true,
                textStyle: {
                color: '#8392A5'
                },
                "type": "slider",
                handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
                handleSize: '80%',
                dataBackground: {
                    areaStyle: {
                    color: '#8392A5'
                    },
                    lineStyle: {
                    opacity: 0.8,
                    color: '#8392A5'
                    }
                },
                handleStyle: {
                    color: '#fff',
                    shadowBlur: 3,
                    shadowColor: 'rgba(0, 0, 0, 0.6)',
                    shadowOffsetX: 2,
                    shadowOffsetY: 2
                },
                "start": 50,
                "end": 100,
                "orient": "horizontal"
            }
        ],
        series: [{
            name: 'price',
            type: 'line',
            //smooth:true,
            sampling:'average',
            areaStyle: {
                normal: {
                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                        offset: 0,
                        color: 'rgba(0, 136, 212, 0.3)'
                    }, {
                        offset: 0.8,
                        color: 'rgba(0, 136, 212, 0.1)'
                    },{
                        offset: 1,
                        color: 'rgba(0, 136, 212, 0)'
                    }], false),
                    shadowColor: 'rgba(0, 0, 0, 0.1)',
                    shadowBlur: 10
                }
            },
            itemStyle: {
                normal: {
                    color: 'rgb(0,136,212)',
                    borderColor: 'rgba(0,136,212,0.2)',
                    borderWidth: 12
                }
            },
            data: []
        }]
    });
    var time = [],
        price = [],
        lastID = 0; 
    //準備好統一的 callback 函數
    var update_mychart = function (data) { //data是json格式的response對象 
        myChart.hideLoading(); // 隱藏加載動畫
        dataLength = data.time.length; 
        if (dataLength == lastID){clearInterval(timeTicket);};//取得數據和上次長度一樣停止ajax
        lastID = dataLength; 
        time = time.slice(dataLength).concat(data.time); 
        price = price.slice(dataLength).concat(data.price.map(parseFloat)); 
        // 填入數據
        myChart.setOption({
            xAxis: {
                data: time
            },
            series: [{
                name: 'price', 
                data: price
            }]
        })
    }
    myChart.showLoading(); // 首次顯示加載動畫
    $.get('/finance').done(update_mychart);
    var timeTicket = setInterval(function () {
        $.post('/finance',{id: lastID}).done(update_mychart);
    }, 3000);
    </script>
</body>
</html>

先運行monitor.py獲取一些數據,再運行app.py,然后打開http://127.0.0.1:5000,就可以看到如下界面:

Login.PNG
My Finance.PNG

源代碼鏈接:http://pan.baidu.com/s/1eSw53we
關注微信公眾號“機智出品”,回復0825,獲取密碼

機智出品.jpg
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容

  • 22年12月更新:個人網站關停,如果仍舊對舊教程有興趣參考 Github 的markdown內容[https://...
    tangyefei閱讀 35,199評論 22 257
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,597評論 25 707
  • 很長時間沒有登錄372165xxx了,莫名的想念,按照Q里的好友分類,一個一個的點開QQ空間,我想我能了解的你們的...
    左寶右貝閱讀 223評論 0 0
  • 人就是以類聚的,和一些人在一起開心,和一些人在一起壓抑。到底是應該隨性而活,還是不得不活。 說完自己都覺得矯情。哈哈
    學著開始閱讀 251評論 0 0
  • 我就是強制(員工使用格力手機)又怎么樣?你是我的員工為什么不用格力手機?你對自己的產品都沒有信心,憑什么讓市場認可...
    龜步也能行千里閱讀 365評論 3 3