代码地址如下:
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\n" % 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\n" % 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\n" % 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\n" % 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\n" % 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\n" % 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

Python-Flask实现基金自选网站的更多相关文章

  1. Python Flask搭建一个视频网站实战视频教程

    点击了解更多Python课程>>> Python Flask搭建一个视频网站实战视频教程 第1章 课程介绍 第2章 预备开发环境 第3章 项目分析.建立目录及模型规划 第4章 建立前 ...

  2. Python flask 构建微电影视频网站☝☝☝

    Python flask 构建微电影视频网站☝☝☝ 1.安装数据库连接依赖包 pip install flask-sqlalchemy 2.创建movie数据库 在CentOS虚拟机,进入MaridD ...

  3. Python flask 构建微电影视频网站✍✍✍

    Python flask 构建微电影视频网站  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大 ...

  4. Python Flask 构建微电影视频网站

    前言 学完本教程,你将掌握: 1.学会使用整形.浮点型.路径型.字符串型正则表达式路由转化器 2.学会使用post与get请求.上传文件.cookie获取与相应.404处理 3.学会适应模板自动转义. ...

  5. Python Flask打造一个视频网站实战视频教程

    下载链接:https://www.yinxiangit.com/607.html 目录: 本套课程从零基础讲解flask开发网站.涉及到的知识点包括:Python和pycharm的安装.urls和视图 ...

  6. 使用wfastcgi在IIS上部署Python Flask应用

    本文介绍了如何在Windows上部署Python Flask应用,相关环境如下: 操作系统:windows 7 Python:3.4 WFastCGI: 2.2 应用所用到的包版本如下: Flask= ...

  7. Flask 构建微电影视频网站(一)

    Flask构建电影视频网站 Python MTV模型 Flask微内核 Flask扩展插件配置及使用方法 根据业务开发网站前后台功能 Flask结合MySQL数据库 你将可以独立开发网站 独立部署运维 ...

  8. VisualStudio2013下安装Python Flask/jade

    为什么是Python? 不做程序的工作好久了,当创业成为工作后越发发现时间的宝贵.时间那么少,需求确实多样的,软件的,web的,还得跨平台,以前熟悉的.Net明显每一项满足的.选来选去还是Python ...

  9. 个人学期总结及Python+Flask+MysqL的web建设技术过程

    一个学期即将过去,我们也迎来了2018年.这个学期,首次接触了web网站开发建设,不仅是这门课程,还有另外一门用idea的gradle框架来制作网页. 很显然,用python语言的flask框架更加简 ...

随机推荐

  1. 第二十八章 springboot + zipkin(brave定制-AsyncHttpClient)

    brave本身没有对AsyncHttpClient提供类似于brave-okhttp的ClientRequestInterceptor和ClientResponseInterceptor,所以需要我们 ...

  2. Nginx如何启用ETag,提高访问速度

    ETag全称EntityTags,HTTP协议规格说明中定义“ETag”为“被请求变量的实体值”. 我们也可以把ETag理解为是一个客户端与服务器关联的记号.这个记号告诉客户端,当前网页在上次请求之后 ...

  3. GetTextMetrics与GetTextExtent的区别

    GetTextMetrics:获取当前字体的信息 GetTextExtent:获取特定的字符串在屏幕上所占的宽度和高度 CDC::GetTextMetrics 作用: 返回当前设备描述表中的当前所用的 ...

  4. TensorFlow实战12:Bidirectional LSTM Classifier

    https://blog.csdn.net/felaim/article/details/70300362 1.双向递归神经网络简介 双向递归神经网络(Bidirectional Recurrent ...

  5. 转:TensorFlow入门(六) 双端 LSTM 实现序列标注(分词)

    http://blog.csdn.net/Jerr__y/article/details/70471066 欢迎转载,但请务必注明原文出处及作者信息. @author: huangyongye @cr ...

  6. Vim 命令、操作、快捷键全集

    Vim是一个类似于Vi的著名的功能强大.高度可定制的文本编辑器,在Vi的基础上改进和增加了很多特性.Vim是自由软件. 命令历史 以:和/开头的命令都有历史纪录,可以首先键入:或/然后按上下箭头来选择 ...

  7. 使用unbound在RHEL7上搭建DNS服务

    1.概念:DNS (Domain Name Server)域名解析服务,使用TCP&UDP的53号端口(主从DNS之间用TCP,客户端查询使用UDP).它可以完成域名与IP地址的互换,可以通过 ...

  8. Spark Structured Streaming:将数据落地按照数据字段进行分区方案

    方案一(使用ForeachWriter Sink方式): val query = wordCounts.writeStream.trigger(ProcessingTime(5.seconds)) . ...

  9. Node.js 笔记(一) nodejs、npm、express安装

    Windows平台下的node.js安装 直接去nodejs的官网http://nodejs.org/上下载nodejs安装程序,双击安装就可以了 测试安装是否成功: 在命令行输入 node –v 应 ...

  10. Office 超级录屏如何旋转视频90度之后保存

    打开视频转换专家   添加视频后点击编辑,然后在旋转的地方设置旋转,应用   输出可以正常播放