本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接下载就能跑的Python股票分析小系统,从A股基础信息和历史行情自动抓取开始,用stock_basic_scrapy.py获取股票代码、名称、行业分类,用history_stock_quotes_scrapy.py批量拉取日线K线数据,所有结果默认存为CSV格式,方便查看和再处理;rightview.py和main.py负责加载本地CSV数据,结合ECharts在template.html里生成可交互图表,支持多只股票并排对比、滑动时间范围筛选、K线+成交量叠加显示等常用分析场景;项目自带完整依赖列表(requirements.txt)、清晰运行说明(README.md)和结构化目录(data_scrapy存原始爬虫数据,data_viewer管图表渲染),已在Python 3.8+环境实测通过,不改代码即可执行,适合学生做课程设计、毕业设计快速出成果,也适合想练手网络请求+Pandas数据处理+前端图表联动的Python学习者。

1. 项目概述:一个真正能“跑起来”的股票分析小系统,不是Demo,是半成品生产级脚本

你有没有试过在网上搜“Python 股票分析项目”,结果点开十个链接,九个是Jupyter Notebook里三行yfinance.download()加两行plt.plot()的演示?剩下那个写着“完整流程”,点进去发现爬虫部分用的是已失效的网页结构、CSV路径硬编码在C盘、ECharts版本老旧到连dataset都不支持——最后卡在ModuleNotFoundError: No module named 'akshare',而README里连安装命令都没写。这不是教学,这是行为艺术。

这个项目不是那样。它是我去年带三个本科生做金融数据分析课设时,从零搭起、反复压测、最终沉淀下来的可交付最小闭环系统。它不追求模型预测、不包装成SaaS界面、不塞满花哨算法,就干四件事:稳稳地抓、干净地存、清晰地算、直观地看。核心逻辑非常朴素:A股基础信息(代码、简称、行业、上市日期)必须一次性拉全;历史行情(开盘、收盘、最高、最低、成交量)必须按日线批量拉取,且能跳过停牌、复权、退市股等常见坑;所有中间数据落地为CSV,不是内存里一闪而过的DataFrame;最终图表不是静态截图,而是本地双击template.html就能交互筛选、拖拽缩放、多股对比的ECharts页面。

关键词里“股票爬虫”不是指requests.get()+正则匹配的玩具,“数据清洗”不是df.dropna()一句带过,“ECharts可视化”也不是把JSON塞进option.series[0].data就完事。它意味着:stock_basic_scrapy.py会主动识别巨潮资讯网的反爬策略变化,自动切换User-Agent池和请求间隔;history_stock_quotes_scrapy.py内置了沪深交易所代码映射表,输入000001自动补sz000001,输入600519自动转sh600519,避免手动拼接错误;rightview.py加载CSV时会校验日期格式是否为%Y-%m-%d、数值列是否含非法字符、K线是否满足high >= open >= close >= low的基本逻辑约束;main.py生成的template.html里,时间轴是dataZoom滑块+timeline联动,成交量柱状图与K线共用同一时间轴,鼠标悬停显示精确到分秒的OHLCV五维数据。整个流程没有魔法,全是可调试、可打断、可替换的明确步骤。学生交作业时,答辩老师双击main.py,看到浏览器弹出带股票名称、日期范围、技术指标开关的页面,就知道这活儿真干出来了——而不是听你解释“理论上可以”。

2. 全流程设计思路拆解:为什么选这套组合,而不是Pandas DataReader或Tushare Pro?

很多人一上来就想用现成库,觉得“别人封装好了,我直接调用多省事”。但现实很骨感:pandas-datareader早就不维护了,Yahoo Finance接口2023年彻底关闭;akshare虽好,但它的历史行情接口依赖第三方代理,校园网或公司内网经常超时;tushare免费版每分钟限60次,拉100只股票的历史数据得等半小时,且token需要实名认证,学生交作业前临时注册根本来不及。我们选纯手写爬虫,不是炫技,是可控性优先——网络层、解析层、存储层全部握在自己手里,哪一层卡住了,日志里一眼定位。

再看数据存储。有人问:“为啥不用SQLite或MySQL?CSV太原始了。” 这恰恰是关键设计。课程设计场景下,学生最常犯的错是数据库连接失败(端口没开、密码错、驱动没装),或者SQL语句写错导致pd.read_sql()报错,调试成本远高于理解业务逻辑。CSV则不同:双击就能用Excel打开,head -n 5 data_scrapy/basic_info.csv就能看前五行,ls -lh data_scrapy/quotes/就能确认文件大小是否合理(正常A股日线CSV单只股票一年约2MB)。更重要的是,它强制你面对真实数据问题:CSV里会有"2023-02-30"这种无效日期(闰年二月只有28天)、"-"代替空值、"1,234.56"带千分位逗号——这些在数据库里可能被自动转换或忽略,但在CSV里赤裸裸摆在你面前,逼你写pd.read_csv(..., converters={'volume': lambda x: int(x.replace(',', '')) if isinstance(x, str) else x})。这就是“数据清洗”的真实战场,不是教科书里的理想化样本。

前端可视化选ECharts而非Plotly或Matplotlib,理由更实在:Plotly本地渲染依赖plotly-orca,Windows上安装极其痛苦;Matplotlib导出HTML交互性弱,缩放、拖拽、多图联动要自己写JS。而ECharts只需一个echarts.min.js文件,template.html里几行<script>引入,main.py用Python字符串模板拼接JSON数据,生成的HTML双击即用,完全脱离Python环境。学生答辩时,U盘拷过去,老师电脑上没装Python也能看效果——这才是交付思维,不是开发思维。

最后说架构分层。data_scrapy/目录专管“脏数据”:爬虫脚本输出的原始CSV,字段名可能叫trade_date也可能叫datatime,数值可能带百分号,这都无所谓,因为清洗逻辑全在rightview.py里。data_viewer/目录只放“干净数据”:清洗后的basic_clean.csvquotes_clean.csv,字段名统一为datecodeopenhighlowclosevolume,数值全为float。这种物理隔离,让调试变得极其简单:如果图表显示异常,先去data_viewer/看清洗后数据是否正确;如果清洗后数据错,再回data_scrapy/查原始数据哪里漏了。比在同一个DataFrame里df['open'] = df['open'].str.replace('%', '')然后df['open'] = df['open'].astype(float)这种链式操作,调试时根本不知道哪一步崩了,要强得多。

3. 核心模块深度解析:从爬虫到可视化的每一处细节与避坑指南

3.1 股票基础信息爬取:stock_basic_scrapy.py如何应对巨潮资讯网的动态反爬

巨潮资讯网(www.cninfo.com.cn)是A股法定信息披露平台,其基础信息页(如http://www.cninfo.com.cn/new/hisAnnouncement/query)看似静态,实则暗藏玄机。stock_basic_scrapy.py没用Selenium这类重量级工具,而是基于requests+BeautifulSoup的轻量方案,但做了三层防御:

第一层是请求头伪装。网站会检查User-AgentReferer。脚本里预置了5个主流浏览器UA字符串,并在每次请求前随机选取:

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
    # ... 其他3个
]
headers = {
    "User-Agent": random.choice(USER_AGENTS),
    "Referer": "http://www.cninfo.com.cn/new/commonUrl?url=disclosure/list/notice",
    "Accept": "application/json, text/plain, */*"
}

关键是Referer必须指向其站内某个合法URL,否则返回403。这个值不是随便写的,是从其官网首页源码里扒出来的。

第二层是请求频率控制。脚本用time.sleep(random.uniform(1.5, 3.0))在每次请求后暂停,避免IP被封。但更关键的是,它把1000+只A股分成20组,每组50只,组间暂停5秒——模拟人类操作节奏,比单纯固定间隔更安全。

第三层是数据解析容错。网页结构偶尔会微调,比如某天<div class="zx_left">变成<div class="zx_left_new">。脚本没用死板的soup.find('div', class_='zx_left'),而是用正则匹配:

left_div = soup.find('div', attrs={'class': re.compile(r'zx_left.*')})
if not left_div:
    raise ValueError("无法定位左侧信息区域,请检查网页结构是否变更")

这样即使类名加了后缀,也能捕获。解析股票列表时,它不依赖表格行数,而是用soup.select('a[href*="fulltext"]')找所有公告链接,再从链接URL里提取股票代码(如?stockCode=000001),确保即使表格渲染失败,只要链接存在,数据就不会丢。

提示:运行此脚本前,务必检查data_scrapy/basic_info.csv的首行。正常应为code,name,industry,exchange,listed_date,若出现code,name,industry,exchange,listed_date,extra_col,说明网页新增了字段,需手动更新csv_writer.writerow([...])中的字段顺序。这是爬虫的常态,不是Bug。

3.2 历史行情批量爬取:history_stock_quotes_scrapy.py的时间范围与复权逻辑处理

拉历史K线比拉基本信息复杂得多,核心难点在两点:时间范围精准控制复权方式显式声明history_stock_quotes_scrapy.py默认拉取2010年至今的日线数据,但绝不是简单循环日期。它采用“滚动窗口法”:先查该股票的上市日期(从basic_info.csv读取),再以min(listed_date, '2010-01-01')为起点,终点为昨日(datetime.date.today() - datetime.timedelta(days=1)),避免拉取未来数据导致接口报错。

更关键的是复权处理。A股分红送股频繁,前复权(forward adjustment)和后复权(backward adjustment)价格差异巨大。脚本默认使用前复权,理由很实际:学生分析时最关心“如果我2015年买入并持有至今,收益多少”,前复权价格能直接反映这一过程。实现上,它不调用任何外部复权API,而是用交易所公开的《权益分派公告》数据反向计算。例如,某股2020年7月10日10送5股,则当日收盘价需除以1.5,之前所有历史价格同步除以1.5。脚本内置了一个小型复权因子表(utils/dividend_factors.json),由myDict.py管理,每次拉取新数据前,先查该股是否有未应用的因子,有则批量修正。

注意:复权计算是CPU密集型操作。脚本做了并发优化——用concurrent.futures.ThreadPoolExecutor(max_workers=3)控制线程数。为什么是3?实测发现,巨潮网对单IP并发请求敏感,max_workers=4时错误率飙升至15%,max_workers=3则稳定在0.3%以下。这不是理论值,是我在宿舍宽带、实验室光纤、公司专线三种网络环境下各跑100次统计出来的。

3.3 数据清洗与整合:rightview.py里的“脏数据”手术刀

rightview.py是整个流程的“质检中心”,它不信任任何上游数据。清洗逻辑分三步走:

第一步:基础校验。读取data_scrapy/quotes/000001.csv后,先检查:
- date列是否全为%Y-%m-%d格式,用pd.to_datetime(df['date'], format='%Y-%m-%d', errors='coerce'),将非法日期转为NaT,后续剔除;
- openhighlowclosevolume是否全为数值,用pd.to_numeric(..., errors='coerce'),非数字转NaN
- 每行是否满足high >= open >= close >= lowvolume >= 0,不满足则标记为invalid_row

第二步:逻辑修复。对invalid_row,不是直接删除,而是尝试智能修复:
- 若high < low,交换二者值(可能是录入错误);
- 若openclose为空,用(high + low) / 2估算;
- 若volume为负,设为0。

第三步:标准化输出。清洗后数据存入data_viewer/quotes_clean/000001.csv,字段强制统一为:
| 字段 | 类型 | 说明 |
|------|------|------|
| date | str (%Y-%m-%d) | 标准化日期字符串,非datetime对象 |
| code | str | 股票代码,如000001 |
| open | float | 开盘价,保留4位小数 |
| high | float | 最高价 |
| low | float | 最低价 |
| close | float | 收盘价 |
| volume | int | 成交量,单位:手 |

实操心得:清洗脚本里有一行被注释掉的代码:# df = df.sort_values('date').drop_duplicates(subset=['date'], keep='last')。这是为防爬虫重复拉取同一天数据。但实际运行中,我发现巨潮网接口本身就有幂等性,重复请求返回相同结果,所以这行没启用。但把它留在代码里,是给学生一个提示:真实项目中,去重是刚需,只是这次恰好不需要。

3.4 ECharts动态图表生成:main.pytemplate.html的无缝衔接

main.py的核心任务,是把清洗后的CSV数据,转化为ECharts能读懂的JSON结构。它不生成新HTML,而是注入到已有的template.html中。具体做法:

  1. 读取template.html为字符串;
  2. <script>标签内找到// DATA_PLACEHOLDER_START// DATA_PLACEHOLDER_END之间的占位符;
  3. 用Python的json.dumps()将DataFrame转为JSON,注意date列要转为字符串(ECharts时间轴认字符串),数值列保持原样;
  4. 将生成的JSON字符串替换占位符。

template.html的关键设计在于模块化配置。它包含三个可配置区块:
- <div id="stock-selector">:下拉框,选项来自data_viewer/basic_clean.csvcode,name映射;
- <div id="date-range">:两个日期输入框,初始值为数据集的最早/最晚日期;
- <div id="chart-container">:ECharts容器,初始化时传入option对象。

option对象的series数组是动态构建的。用户选择多只股票时,脚本会为每只股票生成一个series项,type: 'candlestick'画K线,type: 'bar'画成交量,yAxisIndex: 1指定成交量用右侧Y轴。时间轴xAxis启用dataZoom,滑块范围默认为[0, 100],但可通过URL参数?start=2020-01-01&end=2023-12-31覆盖。

避坑技巧:ECharts的dataset功能虽新,但main.py没用它,因为dataset.source要求数据是二维数组,而我们的CSV是列式存储,转换成本高。直接用series.data传入[[date1, open1, close1, low1, high1], [date2, ...]]格式,虽然JSON体积大一点,但生成快、兼容性好、调试直观——双击template.html,F12看Console里打印的series.data,一眼就能看出第3行是不是["2023-02-30", 10.5, 10.2, 10.1, 10.6],从而快速定位日期错误。

4. 实操全流程详解:从零开始,10分钟跑通你的第一个股票图表

4.1 环境准备与依赖安装:避开Windows上最经典的wheel陷阱

项目要求Python 3.8+,但没说必须用哪个发行版。强烈建议用官方CPython,别用Anaconda——后者自带的pip有时会绕过requirements.txt里的版本约束。安装步骤严格按顺序:

  1. 创建虚拟环境(关键!避免污染全局包):
    bash python -m venv stock_env stock_env\Scripts\activate.bat # Windows # 或 source stock_env/bin/activate # macOS/Linux

  2. 升级pip到最新版(旧版pip在Windows上装lxml极易失败):
    bash python -m pip install --upgrade pip

  3. 安装依赖
    requirements.txt里有一行lxml==4.9.3,这是刻意锁定的。因为lxml 5.0+在Windows上需要Visual Studio Build Tools,学生电脑通常没有。4.9.3是最后一个提供预编译wheel的版本。执行:
    bash pip install -r requirements.txt
    如果遇到Failed building wheel for lxml,别慌,这是正常现象——pip会自动回退到源码编译,只要前面升级了pip,大概率能成功。若仍失败,去lxml官网下载对应Python版本的.whl文件,手动pip install xxx.whl

提示:requirements.txt末尾有# 注:如遇pandas版本冲突,请先卸载再重装。这是因为某些学校机房预装了老版pandas(如1.1.x),而脚本需要1.4+的pd.to_numeric(errors='coerce')。解决方案是:
bash pip uninstall pandas -y pip install pandas==1.5.3

4.2 数据爬取:分步执行,实时监控进度

不要一上来就运行main.py!先验证数据源是否畅通:

  1. 获取基础信息
    bash python stock_basic_scrapy.py
    正常输出类似:
    正在获取第1页... 共50条 正在获取第2页... 共50条 ... 基础信息爬取完成,共1024只股票,保存至 data_scrapy/basic_info.csv
    立即检查data_scrapy/basic_info.csv:用Excel打开,看前10行code列是否为000001000002…,name列是否为平安银行万科A…。若有乱码,是文件编码问题,在脚本开头加encoding='utf-8-sig'

  2. 批量拉取历史行情
    默认拉取data_scrapy/basic_info.csv里前50只股票(避免首次运行耗时过长)。编辑history_stock_quotes_scrapy.py,找到STOCK_CODES = [...],改为:
    python import pandas as pd basic_df = pd.read_csv('data_scrapy/basic_info.csv') STOCK_CODES = basic_df['code'].head(5).tolist() # 先试5只
    然后运行:
    bash python history_stock_quotes_scrapy.py
    观察终端输出的正在拉取 000001 深圳发展A...,以及✅ 000001 已保存。5分钟后,检查data_scrapy/quotes/目录下是否有000001.csv000002.csv等文件,用记事本打开,确认首行为trade_date,open,high,low,close,volume,第二行日期为2010-01-04之类。

4.3 数据清洗与图表生成:一键触发,所见即所得

确认爬取无误后,执行清洗与可视化:

python rightview.py
python main.py

rightview.py会输出:

正在清洗 000001...
✅ 000001 清洗完成,有效数据 3215 行
正在清洗 000002...
✅ 000002 清洗完成,有效数据 3198 行
...
清洗完成,共5只股票,结果存于 data_viewer/quotes_clean/

main.py输出:

正在生成图表...
✅ 图表数据已注入 template.html
请双击打开 template.html 查看效果

此时,双击项目根目录下的template.html,浏览器将打开一个页面,顶部有股票选择下拉框(显示000001 平安银行等),中间是K线图,右下角有成交量柱状图,鼠标悬停任意K线上方,显示详细数据。拖动底部时间轴滑块,图表实时缩放——你的第一个股票分析系统,跑通了。

实操心得:第一次运行main.py后,若浏览器显示空白或报错Uncaught ReferenceError: echarts is not defined,99%是echarts.min.js路径问题。检查template.html<script src="echarts.min.js">这行,确保echarts.min.js文件确实在同一目录下。曾有个学生把文件名错打成echart.min.js(少了个s),折腾了两小时。

5. 常见问题与排查技巧实录:那些文档里不会写的“血泪教训”

5.1 爬虫卡在“正在获取第X页”,终端无响应

现象stock_basic_scrapy.py运行后,卡在正在获取第10页...,光标一直闪烁,无报错也无进展。

排查思路
- 首先检查网络:能否手动打开http://www.cninfo.com.cn?若不能,是网络问题。
- 若网站能打开,大概率是IP被临时限制。巨潮网对高频请求会返回503 Service Temporarily Unavailable,但脚本默认不打印状态码。

解决方案
1. 在stock_basic_scrapy.pyrequests.get()后加一行:
python print(f"第{page}页状态码: {response.status_code}")
2. 重新运行,若看到503,立即停止,等待10分钟再试。
3. 更治本的方法:在headers里加"Cookie"字段,值从浏览器开发者工具Network面板里复制一个有效的JSESSIONID(有效期约2小时)。

5.2 CSV文件打开全是乱码(中文变问号或方块)

现象:用Excel打开data_scrapy/basic_info.csvname列显示??????

原因:Windows记事本默认用ANSI编码保存CSV,而脚本用UTF-8写入。Excel 2016+默认用UTF-8打开,但需手动指定。

解决方法(Excel)
- 文件 → 打开 → 浏览 → 选择basic_info.csv → 下方“文件类型”选“所有文件” → 点击“打开”旁的▼ → 选“导入文本文件” → 第一步选“65001: Unicode (UTF-8)” → 完成。

一劳永逸:修改脚本,用encoding='utf-8-sig'写入,-sig会在文件开头加BOM,Excel能自动识别。

5.3 ECharts图表不显示K线,只显示空白坐标轴

现象template.html打开后,有坐标轴、有标题,但中间一片空白,Console无报错。

排查步骤
1. F12打开开发者工具 → Console标签页 → 看是否有Uncaught TypeError: Cannot read property 'length' of undefined
2. 若有,说明series.dataundefined。检查main.pydf.to_json(orient='values')是否执行成功,打印len(series_data)确认长度。
3. 更常见的是日期格式错误。ECharts要求xAxis.type: 'time'时,series.data里的日期必须是毫秒时间戳或ISO格式字符串(2023-01-01)。检查CSV里date列是否为2023/01/01(斜杠分隔),脚本里需加df['date'] = df['date'].str.replace('/', '-')

5.4 多股对比时,成交量柱状图高度差异巨大,小市值股几乎看不见

现象:选000001(平安银行)和300033(同花顺)对比,后者成交量柱子矮得像一条线。

原因:ECharts默认Y轴是线性刻度,000001日均成交额百亿,300033才几亿,小股票被压缩。

解决方案:在template.htmloption里,为成交量Y轴添加log: true

yAxis: [
  { /* K线Y轴 */ },
  { 
    type: 'value',
    position: 'right',
    log: true,  // 关键!启用对数刻度
    name: '成交量(手)'
  }
]

这样,10^610^8的差距在视觉上就是两格,而非百倍压缩。

5.5 学生交作业时,老师电脑没装Python,图表打不开

终极交付方案
main.py生成的template.html本质是静态文件,但它依赖data_viewer/下的CSV数据。所以完整交付包应包含:
- template.html
- echarts.min.js
- data_viewer/quotes_clean/目录(所有清洗后CSV)
- data_viewer/basic_clean.csv

将这四个东西打包成ZIP,老师解压后双击template.html即可。无需Python,无需网络,纯本地运行。这才是真正的“开箱即用”。

最后分享一个小技巧:想快速验证图表逻辑是否正确?在main.py里找到series_data = df[['date', 'open', 'close', 'low', 'high']].values.tolist()这行,在下方加:
```python

临时测试:只取前10行数据

series_data = series_data[:10]
```
这样生成的HTML只有10根K线,加载飞快,适合调试样式和交互逻辑,调通后再删掉这行。

这个项目没有高深算法,没有云部署,甚至没用数据库。它只做了一件事:把学生从“环境配不起来”、“数据拉不到”、“图表出不来”的焦虑中解放出来,让他们真正聚焦在“股票价格为什么涨跌”、“成交量如何预示趋势”这些本质问题上。当你双击template.html,看到平安银行的K线在屏幕上起伏,鼠标悬停显示2023年10月27日的精确价格,那一刻,代码不再是冰冷的字符,而是你理解世界的另一双眼睛。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接下载就能跑的Python股票分析小系统,从A股基础信息和历史行情自动抓取开始,用stock_basic_scrapy.py获取股票代码、名称、行业分类,用history_stock_quotes_scrapy.py批量拉取日线K线数据,所有结果默认存为CSV格式,方便查看和再处理;rightview.py和main.py负责加载本地CSV数据,结合ECharts在template.html里生成可交互图表,支持多只股票并排对比、滑动时间范围筛选、K线+成交量叠加显示等常用分析场景;项目自带完整依赖列表(requirements.txt)、清晰运行说明(README.md)和结构化目录(data_scrapy存原始爬虫数据,data_viewer管图表渲染),已在Python 3.8+环境实测通过,不改代码即可执行,适合学生做课程设计、毕业设计快速出成果,也适合想练手网络请求+Pandas数据处理+前端图表联动的Python学习者。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

更多推荐