ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python股票数据爬取与可视化分析框架:从K线到指标交互实践

2026/9/10 13:17:10 拓冰建站 浏览量
Python股票数据爬取与可视化分析框架:从K线到指标交互实践 简介面向计算机相关专业毕业设计、课程设计与期末大作业场景这套Python股票数据爬虫分析可视化框架提供完整可运行的项目代码聚焦股票行情数据采集、清洗分析与前端可视化展示适合正在做毕设或需要实战练手的学习者快速上手。压缩包共11个文件以5个Python脚本为核心覆盖爬虫采集、数据处理与可视化主程序另含HTML模板、JavaScript图表库、文本说明和Markdown文档分别支撑页面展示、图表渲染、依赖说明与项目介绍整包仅260KB。目前已有282人学习下载代码经过严格调试确保可运行。框架内各模块职责清晰从行情接口抓取数据、清洗整理到生成交互式可视化图表形成完整技术链路附带项目说明与目录结构整理便于直接作为毕业设计系统使用也能帮助读者理解股票数据爬取、分析到可视化展示的完整流程具有较高的参考与复用价值。1. 从爬虫到图表这套股票数据框架解决了什么先说结论这套项目不是那种只有爬虫、抓完就扔的半成品而是一条「数据采集 → 指标分析 → 页面可视化」的完整链路。你运行main.py之后本地会拉起 Web 服务能直接看到股票行情对应的 K 线、均线和成交量的交互图表。对于做 Python 毕业设计、期末大作业的人来说它最值钱的地方在于——每个环节都有落地的 Python 代码不是 PPT 式的功能罗列。框架里data_scrapy目录负责抓取与清洗data_viewer目录负责读取处理后数据并交给模板渲染utils/myDict.py这类模块则承担路径、请求头、字段映射这类横向逻辑。底层用 requests 做网络请求pandas 处理行情表前端通过本地引入的echarts.min.js画图。对五年以上经验的工程师这套结构也有可拆的地方爬虫层与展示层解耦、配置外置、依赖收敛在requirements.txt稍作扩展就能改成多标的轮询或数据库落盘。这篇文章我会把抓包逻辑、字段清洗、指标计算和视图中枢串起来讲最后给出答辩和调试相关的实测经验。2. 行情爬虫模块从证券列表到历史K线的抓取设计2.1 项目里爬虫模块的分工打开压缩包后能看到两个核心爬虫文件stock_basic_scrapy.py和history_stock_quotes_scrapy.py。前者负责拉股票基础信息代码、名称、所属板块、总市值、流通市值、上市日期后者负责按股票代码抓历史行情开高低收、成交量、成交额。这种拆分在真实业务里也很常见——基础数据更新频率低适合全量刷新行情数据更新频率高适合按代码增量抓取。两个爬虫共享utils/myDict.py里的配置。点开这个文件你会看到请求头、超时时间、需要抓取的股票代码列表、本地缓存路径。很多初学者会把这些值硬编码在爬虫文件里这套项目把它们独立出来职责是清晰的。实际改的时候只需要把股票池替换成自己关注的代码就能跑通一个新场景。2.1.1 基础信息爬虫的抓包思路stock_basic_scrapy.py走的是公开行情接口不需要登录。它先向证券列表接口发起 GET 请求拿到 JSON 后解析里面的总记录数和分页信息再按页数循环拼接 URL。代码结构大致如下import requests import json import pandas as pd def fetch_stock_list(page_size100): base_url https://你的目标行情源/api/stock_list headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), } all_data [] for page in range(1, 6): params { page: page, size: page_size, fields: code,name,industry,total_mv,float_mv,list_date } resp requests.get(base_url, headersheaders, paramsparams, timeout10) data resp.json() records data.get(data, {}).get(list, []) all_data.extend(records) return pd.DataFrame(all_data) if __name__ __main__: df fetch_stock_list() df.to_csv(data_scrapy/stock_basic.csv, indexFalse, encodingutf-8-sig)逻辑说明这里用params而不是手动拼接字符串会让 URL 编码更安全utf-8-sig是为了让 Excel 打开 CSV 时不出现中文乱码。page循环次数是写死的实际你可以根据返回的total字段动态计算总页数后再循环这个项目里用固定值是为了降低答辩时的解释成本。2.2 历史K线接口的参数设计与断点续抓history_stock_quotes_scrapy.py是整条链路里最值得细看的文件。它接收股票代码、开始日期、结束日期三个参数向行情数据接口请求日 K 线然后把返回的列表解析成 DataFrame。请求参数里必须带股票代码和周期字段部分接口还会校验adjust参数——qfq表示前复权hfq表示后复权表示不复权。做技术指标计算时建议用前复权数据否则除权除息会让均线出现假跳变。抓取过程中还做了容错处理。网络超时或接口熔断时会重试失败达到上限就跳过当前代码继续跑最后把成功抓取的部分写入本地文件import time import random import pandas as pd import requests def fetch_history(code, start_date20200101, end_date20241231): url https://你的目标行情源/api/kline headers {User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)} all_rows [] for retry in range(3): try: params { code: code, period: daily, start: start_date, end: end_date, adjust: qfq, } resp requests.get(url, paramsparams, headersheaders, timeout15) if resp.status_code ! 200: raise ValueError(fHTTP {resp.status_code}) quotes resp.json().get(data, {}).get(klines, []) for row in quotes: all_rows.append(row.split(,)) break except Exception as e: print(f[{code}] 第{retry1}次请求失败: {e}) time.sleep(2 * (retry 1)) if all_rows: df pd.DataFrame(all_rows, columns[date, open, close, high, low, volume, amount]) df.to_csv(fdata_scrapy/history_{code}.csv, indexFalse) return len(all_rows)参数说明start和end的格式是YYYYMMDD实际接口也可能接受YYYY-MM-DD具体要看接口文档retry循环里sleep(2 * (retry 1))是常见的指数退避写法第一次失败等 2 秒、第二次等 4 秒比固定 sleep 更不容易被对方限流。抓完一个代码后代码里还会加一句time.sleep(random.uniform(1, 3))这个随机延时是爬虫工程里的常规操作作用是防止请求频率太规律而触发反爬规则。2.3 反爬应对与数据完整性校验这套项目的爬虫定位是「面向学习环境的轻量抓取」没有上代理池和浏览器指纹伪装。但它的请求头、延时、重试逻辑对入门级反爬已经够用。你如果要在真实环境复用有几个地方需要按你的目标站点调整请求头要带完整的User-Agent、Referer、Accept-Language有的接口还会校验Cookie股票池大时建议分批次跑每批之间sleep(30)以上抓回来的数据要做空值检查重点看成交量volume和收盘价close是否有None、、NaN这三种情况。我一般会加一条完整性统计对比接口返回的 K 线数量与交易日历天数如果差太多说明中间有部分数据被限流丢弃需要重新跑这一段日期。这个思路放在答辩里讲出来就是「数据质量校验模块」比单纯说「我抓了数据」要专业得多。3. 数据清洗与指标分析Pandas 计算均线与回撤3.1 为什么抓完不能直接画图很多人拿到历史行情就急着渲染到前端结果画出来的 K 线图缺口很多。原因是接口返回的数据不一定按时间正序排列偶尔还会重复推送当天数据而且部分日期的成交量可能是0停牌或数据源缺失。所以项目里对原始 CSV 做了二次处理这个逻辑分散在utils和data_viewer的读取函数里。清洗步骤可以归纳为三件事第一把date列转成datetime类型并设为索引第二按日期去重并排序第三把字符串型的数字用pd.to_numeric转成浮点数必要时填充停牌日的空值。整理后得到一张标准的 DataFrame列名统一为date, open, close, high, low, volume, amount。3.2 技术指标计算均线、收益率与最大回撤data_viewer/rightview.py里的主要工作是把清洗后的行情数据加工成前端需要的指标序列。最基础的是MA均线通常做MA5、MA20、MA60三条。用 pandas 计算非常直接import pandas as pd # df 是已清洗的日线数据按日期升序 df[ma5] df[close].rolling(window5).mean() df[ma20] df[close].rolling(window20).mean() df[ma60] df[close].rolling(window60).mean() # 日收益率 df[daily_return] df[close].pct_change() # 累计收益率 df[cum_return] (1 df[daily_return]).cumprod() - 1 # 最大回撤 当日累计净值 / 历史累计净值峰值 - 1 df[cum_max] df[cum_return].cummax() df[drawdown] df[cum_return] / df[cum_max] - 1 max_drawdown df[drawdown].min()逻辑说明rolling(window5).mean()表示最近 5 个交易日收盘价的滚动均值常用于短线趋势判断pct_change()是当日对比前一交易日的涨跌幅注意这里的分母是前一日收盘价cummax()用来追踪累计收益的历史最高点最大回撤就是当前收益距离最高点跌了多少这是衡量风险的核心指标。指标参数这里不同策略场景可以调整短线交易看MA5和MA20的金叉死叉中长线看MA60与MA120最大回撤一般控制在-20%以内算稳健。答辩时如果被问「为什么用这些指标」你可以说均线是动量类指标的基础回撤能反映策略在极端行情下的抗风险能力比单纯看收益率更全面。3.3 分析结果的输出形式项目里没有把分析结果落数据库而是直接生成一个大的 JSON传给前端模板。这个设计对课程设计和毕业设计很友好不需要 MySQL 环境部署时省去建表操作。rightview.py里会构造一个类似下面的结构result { code: stock_code, name: stock_name, dates: df[date].astype(str).tolist(), kline: df[[open, close, low, high]].values.tolist(), volumes: df[volume].tolist(), ma5: df[ma5].round(2).tolist(), ma20: df[ma20].round(2).tolist(), ma60: df[ma60].round(2).tolist(), max_drawdown: round(float(max_drawdown), 4), }这样前端拿到的dates、kline、volumes长度一致方便直接映射到 ECharts 的xAxis和series上。有一点要注意df[date].astype(str)会把日期格式化为2024-03-15ECharts 的type: candlestick对这种字符串日期解析没有问题如果直接用datetime对象JSON 序列化会报错。4. Web 可视化层Flask 路由与 ECharts 数据联动4.1 main.py 作为聚合入口整个项目启动后先跑main.py。它做的事情是初始化 Flask 应用注册蓝图将data_viewer里的视图函数挂载到路由上最后app.run()监听本地端口。这样做的好处是爬虫和分析逻辑可以独立测试Web 服务只是作为展示出口不要和爬虫代码耦合在一起。看代码时会发现main.py里没有特别多复杂的逻辑核心是把rightview模块导入并暴露一个/stock/code动态路由。这个设计在真实工程里对应的是「应用入口只做装配不做业务」评审老师问到项目分层时你也能讲得出依据。4.1.1 视图函数的响应流程rightview.py里的核心视图函数接收股票代码后先读取本地清洗好的行情数据然后计算指标最后把 JSON 数据交给模板渲染from flask import render_template, jsonify import pandas as pd def get_stock_data(code): df pd.read_csv(fdata_scrapy/history_{code}.csv, parse_dates[date]) df df.sort_values(date).drop_duplicates(subsetdate) df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() df[ma60] df[close].rolling(60).mean() return df app.route(/stock/code) def stock_view(code): df get_stock_data(code) chart_data build_chart_json(df) # 组装 ECharts 需要的 dict return render_template(template.html, datachart_data)逻辑说明parse_dates[date]让 pandas 在读取时直接完成日期转换省掉后面一步to_datetimedrop_duplicates(subsetdate)保留第一次出现的行重复推送的数据行会被移除。build_chart_json的作用是把已经计算好的均线序列放进返回字典避免在模板里写复杂的 Python 逻辑。4.2 template.html 与 echarts.min.js 的配合这个项目的可视化完全依赖template.html它本地引用了echarts.min.js所以你断网也能跑。模板里接收后端传进来的data然后在前端初始化图表容器。常见写法是在script里用JSON.parse接收后端变量但更稳妥的做法是让 Flask 用| tojson过滤器直接把 Python 字典转换为 JSON 字符串。K 线图和成交量图通常放在同一个grid容器里用dataZoom实现联动缩放var chart echarts.init(document.getElementById(chart)); var raw {{ data | tojson }}; var option { tooltip: { trigger: axis }, legend: { data: [MA5, MA20, MA60] }, grid: [ { left: 60, right: 20, top: 30, height: 55% }, { left: 60, right: 20, top: 72%, height: 20% } ], xAxis: [ { type: category, data: raw.dates, gridIndex: 0 }, { type: category, data: raw.dates, gridIndex: 1 } ], yAxis: [ { scale: true, gridIndex: 0 }, { gridIndex: 1 } ], dataZoom: [ { type: inside, xAxisIndex: [0, 1], start: 50, end: 100 }, { type: slider, xAxisIndex: [0, 1], top: 95% } ], series: [ { name: K线, type: candlestick, data: raw.kline, itemStyle: { color: #ef232a, color0: #14b143, borderColor: #ef232a, borderColor0: #14b143 } }, { name: MA5, type: line, data: raw.ma5, smooth: true, lineStyle: { width: 1 } }, { name: MA20, type: line, data: raw.ma20, smooth: true, lineStyle: { width: 1 } }, { name: 成交量, type: bar, xAxisIndex: 1, yAxisIndex: 1, data: raw.volumes } ] }; chart.setOption(option);参数说明candlestick系列的data每一项必须是[open, close, low, high]的顺序这正是第 3 节里df[[open, close, low, high]]的列顺序顺序反了图形会乱color对应阳线颜色color0对应阴线颜色红涨绿跌是国内行情软件的习惯dataZoom里xAxisIndex: [0, 1]表示上下两个图共享缩放start: 50表示默认只显示后 50% 的数据避免刚打开页面时图形被压缩得看不清。4.3 数据量较大时的渲染策略如果股票超过 3 年日线K 线数据大约有 700 到 800 条ECharts 一次性渲染没有压力。但如果你是抓分钟线数据量会到几万条前端会卡顿。这个项目在模板中已经预设了缩放窗口你可以把start: 50, end: 100改为start: 80, end: 100让默认视角只看最近 20% 区间。另外ECharts 的sampling: lttb可以对折线图数据进行降采样但对 K 线图不适用所以不建议对candlestick做采样保持原始数据才是正确的展示方式。5. 项目落地运行排错、参数调优与毕设文档要点5.1 依赖安装与目录结构核对拿到压缩包解压后先看根目录的requirements.txt里面锁定了项目运行所需的 Python 库。一般包含requests、pandas、flask。安装命令是pip install -r requirements.txt如果你用的是 Anaconda 环境建议先建一个独立环境再装避免和已有包版本冲突。装完后核对目录结构是否能对上├── data_scrapy/ │ ├── stock_basic_scrapy.py │ ├── history_stock_quotes_scrapy.py │ └── utils/myDict.py ├── data_viewer/ │ ├── rightview.py │ └── template.html ├── main.py └── requirements.txtmain.py必须放在项目根目录因为它里面会用相对路径引用data_viewer和data_scrapy。如果你把main.py单独移出来运行会报ModuleNotFoundError这不是代码问题是启动路径问题。5.2 常见运行报错与排查方式第一个高频报错是ModuleNotFoundError: No module named utils。原因是直接运行了data_scrapy目录下的爬虫文件而utils是根目录下的包需要先cd到项目根目录再执行python -m data_scrapy.stock_basic_scrapy。第二个高频问题是 CSV 文件中文乱码这个项目写入时用了utf-8-sig所以一般不会出现如果自己改代码时需要统一编码读取时也要带上encodingutf-8。错误现象排查方向爬虫请求无响应检查目标接口是否要求Cookie请求头的User-Agent是否被识别为 Python 默认值图表不显示打开浏览器控制台看echarts.min.js是否能加载确认data变量已经注入模板日期排序错乱确认读取 CSV 时用了parse_dates另外检查是否有重复行影响排序main.py启动失败看端口是否被占用app.run(port5000)可以改用5001等端口5.3 答辩与课程设计文档的写作建议这套项目拿高分的关键不只是能跑而是文档里能「把技术点讲清楚」。建议围绕三条线组织说明第一爬虫设计写清楚采集对象、接口参数、反爬策略、数据校验方式不要写「我用了爬虫」而是写「本项目通过轮询分页接口结合随机延时与指数退避重试机制实现单标的每日 800 条历史 K 线的高可用抓取」。第二指标分析部分说明均线、收益率、最大回撤这三个指标的计算公式和业务含义最好加一份本地导出的结果对比截图。第三可视化部分解释为什么选 ECharts 而不是 Matplotlib——交互式缩放、K 线组件成熟、支持数据集联动这三点是 ECharts 的优势。如果你想把项目扩展成更好的毕设版本最简单有效的改动是把 CSV 落盘换成 SQLite 存储加一个scheduler定时任务让爬虫每天收盘后自动更新数据。这不会破坏原有框架反而能把程序从「人工触发」升级为「自动运维」在答辩时是很好的加分项。最后提供一个调试技巧Flask 默认的debugTrue能输出完整异常堆栈开发时在你改动过的路由函数上方加app.after_request打印响应耗时能快速定位是读取 CSV 慢还是模板渲染慢。单次 K 线请求响应时间应该控制在 100 毫秒内如果明显偏慢优先检查是不是每次视图调用都重新读全量 CSV考虑设置一个全局缓存字典来控制过期时间。本文还有配套的精品资源点击获取