news 2026/7/11 15:06:41

Day 72:【99天精通Python】金融数据看板 - 数据层实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day 72:【99天精通Python】金融数据看板 - 数据层实现

Day 72:【99天精通Python】金融数据看板 - 数据层实现

前言

欢迎来到第72天!

在昨天的课程中,我们规划了项目的蓝图。今天,我们要开始打地基——构建数据层
一个没有数据的看板就是个空壳。我们需要做两件事:

  1. 定义模型:告诉 Flask 数据库长什么样。
  2. 填充数据:编写脚本,从 BaoStock 接口把股票数据抓回来存进数据库。

本节内容:

  • 配置 Flask-SQLAlchemy
  • 编写models.py
  • 编写数据抓取服务 (services/data_fetcher.py)
  • 初始化数据库并测试抓取

一、配置 Flask 与 数据库

config.py中存放配置信息。

# config.pyimportos BASE_DIR=os.path.abspath(os.path.dirname(__file__))classConfig:# 数据库文件路径SQLALCHEMY_DATABASE_URI='sqlite:///'+os.path.join(BASE_DIR,'finance.db')# 关闭追踪修改,节省内存SQLALCHEMY_TRACK_MODIFICATIONS=False

app.py中初始化 Flask 和 DB。

# app.pyfromflaskimportFlaskfromflask_sqlalchemyimportSQLAlchemyfromconfigimportConfig app=Flask(__name__)app.config.from_object(Config)# 初始化 SQLAlchemydb=SQLAlchemy(app)# 临时路由测试@app.route("/")defindex():return"Finance Board API"if__name__=="__main__":app.run(debug=True)

二、编写数据模型 (models.py)

根据昨天的设计,我们需要两个模型。

# models.pyfromappimportdbclassStockBasic(db.Model):__tablename__='stock_basic'# code 是主键,如 'sh.600519'code=db.Column(db.String(20),primary_key=True)code_name=db.Column(db.String(50))industry=db.Column(db.String(50))update_date=db.Column(db.Date)# 上次更新数据的日期def__repr__(self):returnf"<Stock{self.code_name}>"classStockDaily(db.Model):__tablename__='stock_daily'id=db.Column(db.Integer,primary_key=True)# 建立索引加快查询速度code=db.Column(db.String(20),index=True)date=db.Column(db.Date,index=True)open=db.Column(db.Float)high=db.Column(db.Float)low=db.Column(db.Float)close=db.Column(db.Float)volume=db.Column(db.Float)# 联合唯一索引:同一只股票同一天只能有一条数据__table_args__=(db.UniqueConstraint('code','date',name='unique_code_date'),)

初始化数据库:
打开终端进入项目目录:

python>>>from appimportdb, app>>>with app.app_context():... db.create_all()>>>exit()

此时目录下应该生成了finance.db


三、编写数据抓取服务

我们需要一个独立的模块来处理与 BaoStock 的交互。

新建services/data_fetcher.py

importbaostockasbsimportpandasaspdfromdatetimeimportdatetime,timedeltafromappimportdb,appfrommodelsimportStockBasic,StockDailyfromsqlalchemy.excimportIntegrityErrorclassDataFetcher:def__init__(self):self.system=bs.login()def__del__(self):bs.logout()deffetch_stock_list(self):"""获取所有 A 股列表并存入 StockBasic"""# 获取当天日期date=datetime.now().strftime("%Y-%m-%d")# query_all_stock 接口获取所有股票rs=bs.query_all_stock(day=date)data_list=[]whilers.next():data_list.append(rs.get_row_data())# BaoStock 返回: code, tradeStatus, code_nameforrowindata_list:code,status,name=row# 只存还在交易的股票ifstatus=='1'andcode.startswith(('sh','sz')):# 存入数据库stock=StockBasic.query.get(code)ifnotstock:stock=StockBasic(code=code,code_name=name)db.session.add(stock)db.session.commit()print(f"股票列表更新完成,共{len(data_list)}条")deffetch_daily_data(self,code,start_date=None,end_date=None):"""获取某只股票的日线数据"""ifnotend_date:end_date=datetime.now().strftime("%Y-%m-%d")ifnotstart_date:# 默认抓取过去 1 年start_date=(datetime.now()-timedelta(days=365)).strftime("%Y-%m-%d")rs=bs.query_history_k_data_plus(code,"date,open,high,low,close,volume",start_date=start_date,end_date=end_date,frequency="d",adjustflag="3")data_list=[]whilers.next():data_list.append(rs.get_row_data())# 批量入库forrowindata_list:# row: [date, open, high, low, close, volume]try:daily=StockDaily(code=code,date=datetime.strptime(row[0],"%Y-%m-%d").date(),open=float(row[1]),high=float(row[2]),low=float(row[3]),close=float(row[4]),volume=float(row[5])ifrow[5]else0)db.session.add(daily)exceptValueError:continue# 跳过无效数据try:db.session.commit()print(f"{code}数据导入成功,共{len(data_list)}条")exceptIntegrityError:db.session.rollback()print(f"{code}数据部分重复,已回滚")# 测试代码if__name__=="__main__":# 需要在 app context 下运行,因为用到了 dbwithapp.app_context():fetcher=DataFetcher()# 1. 更新股票列表 (跑一次就行)# fetcher.fetch_stock_list()# 2. 更新茅台数据fetcher.fetch_daily_data("sh.600519")

四、运行与验证

  1. 运行python services/data_fetcher.py
  2. 观察控制台输出,确认数据下载成功。
  3. 使用 DB Browser 打开finance.db,查看stock_daily表,应该能看到一整年的茅台股价数据。

五、小结

query_all_stock

query_history_k

db.session.add

DataFetcher

BaoStock API

Models

股票列表

日线数据

SQLite

关键要点

  1. ORM 模型StockBasic存元数据,StockDaily存时间序列数据。
  2. UniqueConstraint:数据库层面的约束,防止同一天插入两条重复数据。
  3. App Context:在独立的脚本中使用 Flask 的db对象时,必须包裹在app.app_context()中。

六、课后作业

  1. 完善抓取逻辑:目前的fetch_stock_list比较慢,尝试只抓取我们感兴趣的几只股票(如上证50)。
  2. 断点更新:修改fetch_daily_data,每次抓取前先查询数据库,找出这只股票最新的一条数据的日期,然后只抓取那个日期之后的数据(增量更新)。
  3. 异常处理:如果在抓取过程中网络断了怎么办?给fetcher增加重试机制。

下节预告

Day 73:金融数据看板 - 后端接口与数据分析- 数据有了,明天我们写 API 接口,并计算 MA 均线,为前端绘图提供 JSON 数据。


系列导航

  • 上一篇:Day 71 - 项目篇开篇
  • 下一篇:Day 73 - 金融数据看板后端逻辑(待更新)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 12:48:58

一键启动Qwen3-Reranker:Gradio可视化界面快速体验

一键启动Qwen3-Reranker&#xff1a;Gradio可视化界面快速体验 1. 引言 在检索增强生成&#xff08;RAG&#xff09;系统中&#xff0c;重排序&#xff08;Reranking&#xff09;是提升检索质量的关键环节。通过将初步召回的候选文档进行精细化排序&#xff0c;能够显著提高最…

作者头像 李华
网站建设 2026/7/8 15:54:11

LangFlow交通运输:路线优化与调度建议系统实战

LangFlow交通运输&#xff1a;路线优化与调度建议系统实战 1. 引言 随着城市化进程加快和物流行业的迅猛发展&#xff0c;交通运输中的路线优化与调度决策成为提升效率、降低成本的关键环节。传统调度系统依赖规则引擎或静态算法&#xff0c;在面对动态交通状况、多目标优化需…

作者头像 李华
网站建设 2026/7/10 6:27:44

Fun-ASR语音文化传承:少数民族语言的保护性转录工程

Fun-ASR语音文化传承&#xff1a;少数民族语言的保护性转录工程 1. 引言 在全球化快速发展的背景下&#xff0c;语言多样性正面临前所未有的挑战。据联合国教科文组织统计&#xff0c;全球约有40%的语言处于濒危状态&#xff0c;其中少数民族语言尤为脆弱。语言不仅是交流工具…

作者头像 李华
网站建设 2026/7/10 6:27:44

通义千问2.5-7B房地产:户型分析与描述生成

通义千问2.5-7B房地产&#xff1a;户型分析与描述生成 1. 引言 1.1 业务场景描述 在房地产行业&#xff0c;楼盘推广、线上平台展示和客户沟通高度依赖对户型图的精准解读与生动描述。传统方式下&#xff0c;房产文案撰写依赖人工经验&#xff0c;耗时长、成本高&#xff0c…

作者头像 李华
网站建设 2026/7/7 13:22:41

实测阿里Paraformer模型,识别速度达5倍实时太强了

实测阿里Paraformer模型&#xff0c;识别速度达5倍实时太强了 1. 引言&#xff1a;中文语音识别的新选择 随着人工智能技术的快速发展&#xff0c;自动语音识别&#xff08;ASR&#xff09;在会议记录、访谈转写、语音输入等场景中扮演着越来越重要的角色。在众多开源ASR方案…

作者头像 李华