• Python-Flask实现基金自选网站


    代码地址如下:
    http://www.demodashi.com/demo/14734.html

    项目介绍

    本项目的基金数据来自天天基金网,但该网站用户体验较差,内容冗余,故自己实现一个轻量级网站,从8个指标维度对股票基金和债券基金进行挑选,可对进行收益进行排序,并且可查看基金的同类排名与历史涨跌幅,方便快捷的筛选选出优质基金进行投资,最大化收益率。

    网站已部署在 http://134.175.24.108:52571 可点击链接查看效果,为了不影响其他用户的体验,部署的网站只显示前100只基金的数据,删除和自选的操作使用了本地存储,存在浏览器端。下载的代码压缩包中包含了本地存储和服务端存储两种版本。

    项目截图如下:

    项目文件结构

    fund     # 代码文件夹
    ├── exclude.txt              # 已删除基金列表
    ├── own.txt                   # 自选基金列表
    ├── requirements.txt     # python第三方库依赖文件
    ├── fund.py                   # 服务端文件
    ├── static                      # 静态文件夹
    │   ├── highstock.js
    │   ├── index.css
    │   ├── index.js
    │   └── jj.png
    └── templates              # 模板文件夹,存放html页面
        └── index.html
    

    此外还有一个fund_local文件夹,结构与fund文件夹一致,其中的代码为本地存储版本,使用localStorage

    项目运行

    1. 安装python
    2. 安装python第三方依赖库,项目文件夹中有依赖文件 pip install -r requirements.txt,依赖的库主要有flask,flask-bootstrap,pandas,gevent,requests。
    3. 运行 python fund.py,成功运行会输出 Listening at http://localhost:52571
    4. 访问 http://localhost:52571

    代码实现

    使用flask框架,网站前端框架使用bootstrap,表格控件为bootstrap-table,趋势图为highstock,文章最后有用到几个框架的文档网址,可自行阅读。

    后端实现

    首先进行初始化

    def __init__(self, fund_type, num=100):
            self.app = Flask(__name__)
            self.num = num             # 基金数量
            self.last_time = 0         # 最后一次更新时间
            self.app.debug = True
            self.fund_type = fund_type # 基金类型,(股票,债券或者混合型)
            self.GetExcludeAndOwn()    # 获取已删除和自选的基金列表
            self.GetData()             # 获取数据
    
            Bootstrap(self.app)        # 初始化bootstrap
    

    初始化完成之后

        # 读取文件,获取删除的基金和自选的基金
        def GetExcludeAndOwn(self):
            with open("exclude.txt", "a+") as fs:
                self.exclude = map(lambda x: x.strip(), fs.readlines())
    
            self.exclude = set(self.exclude)
            print("exclude id: ", self.exclude)
    
            with open("own.txt", "a+") as fs:
                self.own = map(lambda x: x.strip(), fs.readlines())
    
            self.own = set(self.own)
            print("own id: ", self.own)
    

    从天天基金网获取基金数据,通过抓包得到数据的链接,爬虫与抓包这里不做展开,有兴趣可单独联系。
    基金数据每天更新一次,获取到基金数据之后,存入DateFrame,去掉多余的列,只保留8个指标,然后将数据转为html,方便后面直接返回给前端展示。

    # 获取近一年基金数据
        def GetData(self):
            now = time.time()
            if (now - self.last_time < 60 * 60 * 24):  # 24小时更新一次
                return self.fund_table
    
            self.last_time = now
            s = requests.Session()
            now = datetime.today()
    
            # 起始时间为一年前的今天,结束时间为今天
            sd = date2str(now - timedelta(days=365)).strftime('%Y-%m-%d')
            ed = date2str(now).strftime('%Y-%m-%d')
    
            res = s.get('http://fund.eastmoney.com/data/rankhandler.aspx?op=ph&dt=kf&ft=%s&rs=&gs=0&sc=1yzf&st=desc&sd=%s&ed=%s&qdii=|&tabSubtype=,,,,,&pi=1&pn=%d&dx=1&v=%lf' %
                        (self.fund_type, sd, ed, self.num, random())).content
    
            res = res[res.find("["):res.rfind("]") + 1]
            obj = json.loads(res)
            arr = []
            for fund in obj:
                arr.append(fund.split(",")[:-4])
    
            columns = ["基金代码", "基金简称", "", "", "单位净值", "累计净值", "日增长率", "近1周", "近1月",
                       "近3月", "近6月", "近1年", "近2年", "近3年", "今年来", "成立来", "", "", "", "", "手续费"]
            self.fund_df = pd.DataFrame(arr, columns=columns)
            # 去掉多余的列
            self.fund_df.drop(self.fund_df.columns[[-2, -3, -4, -5, -6, -7, 2, 3, 4, 5, 6]],
                              axis=1, inplace=True)
            self.fund_df.insert(0, "checkbox", "")
    
            # 自选基金数据
            self.own_df = self.fund_df[self.fund_df['基金代码'].isin(self.own)]
            self.own_table = self.own_df.to_html(index=False)
    
            # 其他基金数据
            self.fund_df = self.fund_df[~self.fund_df['基金代码'].isin(self.exclude)]
            self.fund_table = self.fund_df.to_html(index=False)
    
            return self.fund_table
    

    设置路由,监听前端请求,删除基金的时候,将基金代码写入文件并更新基金数据,下次请求将不再返回已删除的基金

          @self.app.route("/fund", methods=["GET", "DELETE"])
            def fund():
                # 查看全部基金
                if request.method == "GET":
                    return self.GetData()
    
                # 删除选中的基金
                else:
                    id_list = request.form.getlist("id[]")
                    self.fund_df = self.fund_df[~self.fund_df['基金代码'].isin(id_list)]
                    self.fund_table = self.fund_df.to_html(index=False)
                    self.exclude |= set(id_list)
    
                    with open("exclude.txt", "w") as fs:
                        fs.writelines(map(lambda x: "%s
    " % x, self.exclude))
    
                    return "OK"
    

    关于自选基金,加自选时,将基金代码添加到自选基金列表,并且添加到已删除列表,保证记录的唯一性。取消自选时从自选列表删除,重新在全部基金里显示。

    @self.app.route("/fund", methods=["GET", "DELETE"])
            def fund():
                # 查看全部基金
                if request.method == "GET":
                    return self.GetData()
    
                # 删除选中的基金
                else:
                    id_list = request.form.getlist("id[]")
                    self.fund_df = self.fund_df[~self.fund_df['基金代码'].isin(id_list)]
                    self.fund_table = self.fund_df.to_html(index=False)
                    self.exclude |= set(id_list)
    
                    with open("exclude.txt", "w") as fs:
                        fs.writelines(map(lambda x: "%s
    " % x, self.exclude))
    
                    return "OK"
    
            @self.app.route("/own", methods=["GET", "POST", "DELETE"])
            def own():
                # 查看自选基金
                if request.method == "GET":
                    return self.own_table
    
                # 加自选
                if request.method == "POST":
                    id_list = request.form.getlist("id[]")
    
                    self.own_df = self.own_df.append(
                        self.fund_df[self.fund_df['基金代码'].isin(id_list)]
                    )
                    self.own_table = self.own_df.to_html(index=False)
                    self.own |= set(id_list)
                    with open("own.txt", "w") as fs:
                        fs.writelines(map(lambda x: "%s
    " % x, self.own))
    
                    self.fund_df = self.fund_df[
                        ~self.fund_df['基金代码'].isin(id_list)
                    ]
                    self.fund_table = self.fund_df.to_html(index=False)
                    self.exclude |= set(id_list)
    
                    with open("exclude.txt", "w") as fs:
                        fs.writelines(map(lambda x: "%s
    " % x, self.exclude))
    
                    return "OK"
    
                # 取消自选
                else:
                    id_list = request.form.getlist("id[]")
    
                    self.fund_df = self.own_df[
                        self.own_df['基金代码'].isin(id_list)
                    ].append(self.fund_df)
                    self.fund_table = self.fund_df.to_html(index=False)
                    self.exclude -= set(id_list)
    
                    with open("exclude.txt", "w") as fs:
                        fs.writelines(map(lambda x: "%s
    " % x, self.exclude))
    
                    self.own_df = self.own_df[
                        ~self.own_df['基金代码'].isin(id_list)
                    ]
                    self.own_table = self.own_df.to_html(index=False)
                    self.own -= set(id_list)
                    with open("own.txt", "w") as fs:
                        fs.writelines(map(lambda x: "%s
    " % x, self.own))
    
                    return "OK"
    

    前端实现

    页面加载时向后台请求数据,然后使用bootstrap-table进行表格渲染

    // 获取全部基金数据
    $.ajax({
      url: '/fund',
      type: 'GET',
      success: function (res) {
        $("#panel-all").html(res);
        $all_table = $('#panel-all table');
        $all_table.bootstrapTable(Object.assign({ toolbar: "#all-toolbar" }, fund_table_option));
        $("#all-toolbar").show();
      }
    });
    
    // 获取自选基金数据
    $.ajax({
      url: '/own',
      type: 'GET',
      success: function (res) {
        $("#panel-own").html(res);
        $own_table = $('#panel-own table');
        $own_table.bootstrapTable(Object.assign({ toolbar: "#own-toolbar" }, fund_table_option));
        $("#own-toolbar").show();
      }
    });
    

    基金删除,加自选,取消自选

    // 删除选中的基金
    $("#delete").click(function () {
      var selections = $all_table.bootstrapTable('getAllSelections').map(function (row) {
        var uid = row["基金代码"];
        $all_table.bootstrapTable("removeByUniqueId", uid);
        return uid;
      });
    
      $.ajax({
        url: '/fund',
        type: 'DELETE',
        data: { "id": selections },
        success: function () { }
      });
    });
    
    // 单只基金 加自选
    var add_own = function (code, ajax) {
      var row = $all_table.bootstrapTable("getRowByUniqueId", code);
      row["checkbox"] = false;
      $own_table.bootstrapTable("append", row);
      $all_table.bootstrapTable("removeByUniqueId", code);
      if (ajax) {
        $.ajax({
          url: '/own',
          type: 'POST',
          data: { "id": [code] },
          success: function () { }
        });
      }
    };
    
    // 选中的基金 批量加自选
    $("#batch_add_own").click(function () {
      var selections = $all_table.bootstrapTable('getAllSelections').map(function (row) {
        var uid = row["基金代码"];
        add_own(uid, false);
        return uid;
      });
      $.ajax({
        url: '/own',
        type: 'POST',
        data: { "id": selections },
        success: function () { }
      });
    });
    
    var del_own = function (code, ajax) {
      var row = $own_table.bootstrapTable("getRowByUniqueId", code);
      row["checkbox"] = false;
      $all_table.bootstrapTable("prepend", row);
      $own_table.bootstrapTable("removeByUniqueId", code);
      if (ajax) {
        $.ajax({
          url: '/own',
          type: 'DELETE',
          data: { "id": [code] },
          success: function () { }
        });
      }
    };
    
    // 选中的基金取消自选
    $("#batch_del_own").click(function () {
      var selections = $own_table.bootstrapTable('getAllSelections').map(function (row) {
        var uid = row["基金代码"];
        del_own(uid, false);
        return uid;
      });
    
      $.ajax({
        url: '/own',
        type: 'DELETE',
        data: { "id": selections },
        success: function () { }
      });
    });
    

    在本地存储的版本中,以上操作不需要与后台交互,不需发送ajax请求,只需将变化的基金代码列表存入localStorage。如果对flask不太了解的话,建议先熟悉local版的代码,后台代码较为简单,熟悉了之后再阅读完整的版本。

    参考网站:

    天天基金网:
    http://fund.eastmoney.com/
    flask中文文档:
    https://dormousehole.readthedocs.io/en/latest/
    bootstrap-table中文文档:
    http://bootstrap-table.wenzhixin.net.cn/zh-cn/documentation/
    highstock演示:
    https://www.hcharts.cn/demo/highstock
    Python-Flask实现基金自选网站

    代码地址如下:
    http://www.demodashi.com/demo/14734.html

  • 相关阅读:

    20145309《网络对抗》网络欺诈技术防范
    ceshi
    20145306 网路攻防 web安全基础实践
    20145306 张文锦 网络攻防 web基础
    20145306张文锦 网络欺诈技术防范
    20145306 《网络攻防》 信息搜集与漏洞扫描
    20145306 《网络攻防》 MSF基础应用
    20145306张文锦《网络对抗》恶意代码分析
    20145306 《网络攻防》 免杀技术
  • 原文地址:https://www.cnblogs.com/demodashi/p/10480161.html
Copyright © 2020-2023  润新知