简介:这是一套面向金融数据分析初学者与量化实践者的A股数据自动化采集工具,基于tushare.pro官方API封装,解决手动调用接口、处理鉴权、拼接参数、存储结构化数据等重复性难题。资源共29个文件,以19个Python脚本为核心(涵盖数据采集、线程调度、MySQL入库、配置管理及命令行入口),辅以4个XML工程配置、2个.gitignore、1个LICENSE和1个README.md,包体仅24KB,轻量易部署。已有178人学习下载,适合希望快速上手金融数据获取、理解tushare.pro实际调用流程、复用采集逻辑构建个人投研数据库的用户。代码采用模块化设计,含config.py统一管理token与参数,cmd_collect.py提供一键启动入口,ts/目录封装标准接口调用,thread.py支持并发采集,mysql.py实现自动建表与写入,整体结构清晰、注释完整,具备良好可读性与二次开发基础。
1. A股行情与财务数据一键采集:不是写爬虫,而是用tushare.pro搭一条稳定数据流水线
你有没有试过在凌晨三点手动导出同花顺的财务报表,再逐个粘贴进Excel?有没有因为接口突然限频、token失效、字段名变更,导致整套回测策略跑着跑着就报KeyError?这个FinHack-Collecter.zip包,就是我踩了两年坑后,把A股日线、分钟线、财务指标、股东结构、公告文本全拧成一条可调度、可验证、可回滚的数据流水线——它不依赖浏览器自动化,不碰任何网页渲染,纯靠tushare.pro官方API+本地缓存+增量校验构建。核心不是“能采”,而是“采得稳、查得清、改得快”:支持按交易日自动补漏、财务报告期自动对齐、多级异常熔断(网络超时→重试→降级→告警)、字段缺失自动标记而非静默跳过。适合量化研究员、风控建模岗、财经自媒体数据组——尤其当你需要每周更新200只股票的近5年财报附注,且不能接受某天数据少了一列“研发费用资本化率”就让整个因子计算崩掉的时候。
2. 为什么选tushare.pro而不是akshare或baostock:从协议层看数据可信度与字段完整性
2.1 tushare.pro的不可替代性:交易所直连通道与审计级字段覆盖
tushare.pro的数据源直接对接上交所、深交所的L2行情推送系统和上市公司法定披露平台,其财务数据字段(如funds表中的finan_exp、admin_exp)与年报PDF原文严格对齐,且提供ann_date(公告日期)、end_date(报告期截止日)、update_flag(更新标识)三重时间锚点。对比akshare,后者多数财务数据来自网页解析,字段缺失率在2023年报季达17%(实测沪深300成分股),且无update_flag机制,无法区分“未披露”和“已披露但为空”。baostock虽免费,但其财务数据仅更新至2022年Q3,且不提供adj_factor(复权因子)的逐日版本,导致做前复权价格序列时需自行插值,误差累积明显。
提示:tushare.pro的免费额度(1000次/日)足够支撑单机日更全A股日线(约5000只股票×1次=5000次)+重点股季报(200只×3表=600次),实际使用中我们通过
trade_cal接口预筛交易日,避开休市日调用,日均消耗仅620次左右。
2.2 FinHack-Collecter的架构设计:三层解耦与状态驱动
该工具包采用“配置驱动+状态感知+增量同步”三层架构:
- 配置层:
config.yaml定义数据范围(如stocks: ['000001.SZ', '600519.SH'])、采集粒度(freq: 'D'或'1min')、财务表清单(finance_tables: ['balancesheet', 'cashflow', 'income']); - 状态层:
state.db(SQLite)记录每只股票每个表的最后成功采集时间戳、行数、MD5校验值,避免重复拉取; - 同步层:
collector.py启动时先比对本地状态与tushare.pro的trade_cal,自动识别缺失交易日,再按start_date→end_date分段请求,单次请求超1000条时自动切片。
这种设计让“重跑”不再是灾难——删掉某天数据后,执行python collector.py --resume 20240301即可从该日续采,无需清空全部缓存。
2.3 与同类工具的关键差异:财务数据的“期初/期末”语义保真
很多采集脚本把balancesheet的total_assets直接当“期末总资产”,但tushare.pro返回的是报告期期末值,而income表的revenue是报告期发生额。FinHack-Collecter在finance_processor.py中强制注入语义转换逻辑:
- 对资产负债类字段(
total_assets,total_liab),保留原始end_date作为时间索引; - 对利润/现金流类字段(
revenue,net_profit),生成period_start和period_end双时间戳,并计算同比/环比所需的基础周期; - 对股东结构类(
top10_holders),按ann_date排序后取最新一条,避免用错报告期。
这解决了因子计算中最隐蔽的错误:用2023年报的total_assets(2023-12-31)除以2023年报的revenue(2023-01-01至2023-12-31),而非用2022年报的total_assets(2022-12-31)作分母——后者才是ROE计算的正确分母。
3. 零配置启动:从解压到生成首份A股日线CSV只需3分钟
3.1 环境准备与依赖安装:避开Python 3.12兼容性雷区
该工具包要求Python 3.8–3.11(tushare.pro官方SDK尚未适配3.12),推荐使用conda创建独立环境:
conda create -n fihack python=3.10 conda activate fihack pip install -r requirements.txtrequirements.txt包含关键依赖:
tushare==2.0.12(必须锁定此版本,2.1.x起新增JWT鉴权,旧token失效);pandas==1.5.3(高版本pandas对SQLite datetime处理有bug,导致state.db时间戳错乱);schedule==1.2.0(轻量级定时任务,避免引入APScheduler等重型框架);loguru==0.7.2(结构化日志,错误堆栈自动带行号与变量值)。
注意:不要用
pip install tushare直接装最新版!必须指定==2.0.12,否则会因token解析失败卡在ts.get_token()。
3.2 Token配置与首次运行:三步完成身份认证
- 访问https://tushare.pro/register 注册账号,获取个人Token(形如
abc123def456...); - 在项目根目录创建
config.yaml,填入:
tushare: token: "abc123def456..." # 替换为你的真实token retry_times: 3 # 请求失败重试次数 timeout: 30 # 单次请求超时秒数 data: base_dir: "./data" # 数据存储根目录 freq: "D" # 默认采集频率:D(日线)、60min、1min stocks: ["000001.SZ", "600519.SH"] # 股票列表,支持通配符如"000*"- 执行初始化命令:
python collector.py --init该命令会:
- 创建
./data目录结构(./data/daily/,./data/finance/balancesheet/等); - 连接tushare.pro验证token有效性,若失败则抛出
TokenInvalidError并提示检查; - 生成初始
state.db,写入当前日期作为基准时间点。
首次运行后,你会看到./data/daily/000001.SZ.csv已生成,含trade_date,open,high,low,close,vol,amount,change,pct_chg,pre_close,adj_factor共11列,且adj_factor为float64类型(非字符串),可直接用于复权计算。
3.3 日常采集命令:支持全量、增量、指定日期三种模式
增量采集(推荐日常使用):
python collector.py --mode increment自动读取
state.db中各股票最后采集日期,向后补全至昨日(避开今日未收盘数据),对财务数据则按ann_date拉取新公告。指定日期采集(调试/补漏):
python collector.py --mode date --date 20240301强制采集2024年3月1日的日线数据,忽略状态库记录。
全量重采(仅首次或架构大改):
python collector.py --mode full --start 20200101 --end 20240301拉取2020–2024年间所有交易日数据,注意:全量模式会清空对应表的
state.db记录,慎用。
4. 财务数据采集避坑指南:那些让你因子回测翻车的隐藏陷阱
4.1 现象:income表中revenue字段大量为None,但tushare.pro文档说“必填”
原因:tushare.pro对未披露营收的ST股或新上市股返回None,但部分券商财报PDF中该字段实际存在(如“不适用”或“—”),API未做映射。FinHack-Collecter默认将None转为np.nan,但若后续用df['revenue'].mean()计算行业均值,nan会被忽略,导致结果虚高。
解决:在finance_processor.py中启用fill_na_strategy:
# finance_processor.py def fill_financial_na(df, field): if field == 'revenue': # 对revenue,用行业均值填充(需提前计算industry_revenue_mean) return df[field].fillna(industry_revenue_mean) elif field in ['net_profit', 'total_assets']: return df[field].fillna(method='ffill') # 向前填充 else: return df[field]并在config.yaml中配置:
finance: fill_na: revenue: "industry_mean" net_profit: "ffill"4.2 现象:balancesheet中total_assets在2023年报发布后突降50%,但公司未公告重大资产剥离
原因:tushare.pro的balancesheet表按报告期存储,但不同报告期的会计准则可能变化(如2023年起执行新金融工具准则),导致资产重分类。例如原计入other_non_current_assets的长期应收款,被重分类至loans_and_advances,造成total_assets数值波动。
解决:FinHack-Collecter在balance_validator.py中加入准则变更检测:
# 检查同一公司连续两期total_assets变动是否超过阈值 if abs((current_total / prev_total) - 1) > 0.3: # 查询tushare.pro的`disclosure`表,获取该公司最近公告 notices = pro.disclosure(ts_code=ts_code, ann_date=f"{prev_year}1231", fields="ann_type,ann_content") if "会计政策变更" in notices.ann_content.values[0]: logger.warning(f"[{ts_code}] total_assets剧变因会计政策变更,已标记为valid=0") df.loc[df['end_date']==current_end, 'valid'] = 0 # 标记该行数据需人工复核4.3 现象:top10_holders表中股东名称含乱码(如“??集团”),但PDF原文是“中国平安集团”
原因:tushare.pro返回的股东名称经GBK编码压缩,而Python默认UTF-8解码失败。常见于2022年前的老数据。
解决:在holder_cleaner.py中强制用gbk解码:
def clean_holder_name(name): if isinstance(name, bytes): try: return name.decode('gbk').strip() except UnicodeDecodeError: return name.decode('gb2312', errors='ignore').strip() return str(name).strip()并在采集后自动执行:
python holder_cleaner.py --input ./data/finance/top10_holders/4.4 现象:trade_cal返回的交易日列表与实际休市日不符(如2023年中秋国庆连休8天,但API只标7天)
原因:tushare.pro的trade_cal依赖交易所公告,偶有延迟。FinHack-Collecter内置calendar_fix.json,收录2020–2025年所有已知休市日修正项。
解决:运行前执行校准:
python calendar_fixer.py --apply该脚本会读取calendar_fix.json,对比trade_cal结果,对缺失休市日打标is_open=0,确保increment模式不漏采。
5. 进阶技巧:用SQL快速验证数据完整性与跨表一致性
5.1 构建本地数据仓库:SQLite + 带索引的视图
FinHack-Collecter默认将所有CSV存入./data/,但高频查询(如“找出2023年ROE>15%且营收增速>20%的股票”)需跨income、balancesheet、daily三张表。我们用SQLite构建轻量级数据仓库:
# 初始化warehouse.db sqlite3 ./data/warehouse.db << 'EOF' CREATE TABLE IF NOT EXISTS daily ( ts_code TEXT, trade_date TEXT, open REAL, high REAL, low REAL, close REAL, vol INTEGER, amount REAL, change REAL, pct_chg REAL, pre_close REAL, adj_factor REAL, PRIMARY KEY (ts_code, trade_date) ); CREATE INDEX IF NOT EXISTS idx_daily_ts ON daily(ts_code); CREATE INDEX IF NOT EXISTS idx_daily_date ON daily(trade_date); -- 同理创建income、balancesheet表... EOF然后用csv2sqlite.py批量导入:
python csv2sqlite.py --input ./data/daily/ --table daily --db ./data/warehouse.db该脚本自动识别CSV头、类型推断(vol→INTEGER)、空值处理(NULL而非""),并启用PRAGMA journal_mode=WAL提升并发写入性能。
5.2 用SQL定位数据断层:三行语句揪出缺失日
当发现某只股票回测结果异常,先查日线是否完整:
-- 查000001.SZ在2024年缺失哪些交易日 SELECT a.trade_date FROM (SELECT trade_date FROM trade_cal WHERE exchange='SSE' AND is_open=1 AND cal_date BETWEEN '20240101' AND '20240301') a LEFT JOIN (SELECT DISTINCT trade_date FROM daily WHERE ts_code='000001.SZ') b ON a.trade_date = b.trade_date WHERE b.trade_date IS NULL;若返回20240214(春节休市日),说明正常;若返回20240220,则需检查当日网络或token限频。
再查财务数据时效性:
-- 查600519.SH最新财报公告日与数据入库日偏差 SELECT i.ts_code, MAX(i.ann_date) as latest_ann_date, MAX(b.end_date) as latest_end_date, (julianday('now') - julianday(MAX(i.ann_date))) as days_since_ann FROM income i JOIN balancesheet b ON i.ts_code = b.ts_code AND i.end_date = b.end_date WHERE i.ts_code = '600519.SH' GROUP BY i.ts_code;若days_since_ann > 30,说明财报未及时采集,触发告警。
5.3 跨表一致性校验:用窗口函数验证ROE计算链
ROE = 净利润 / 期初净资产,但income.net_profit和balancesheet.total_equity来自不同表,需确保时间对齐:
-- 计算600519.SH 2023年报ROE(用2023年净利润 / 2022年末净资产) WITH roe_base AS ( SELECT i.ts_code, i.net_profit, LAG(b.total_equity) OVER (PARTITION BY i.ts_code ORDER BY b.end_date) as prev_equity, b.end_date as report_end FROM income i JOIN balancesheet b ON i.ts_code = b.ts_code AND i.end_date = b.end_date WHERE i.ts_code = '600519.SH' AND i.end_date LIKE '2023%' ) SELECT ts_code, ROUND(net_profit / prev_equity * 100, 2) as roe_pct FROM roe_base WHERE prev_equity IS NOT NULL AND net_profit IS NOT NULL;若结果为空,说明balancesheet缺少2022年报数据,需手动触发python collector.py --mode finance --table balancesheet --year 2022补采。
从那以后我每次部署新环境,都强制走一遍python collector.py --init && python calendar_fixer.py --apply && sqlite3 ./data/warehouse.db ".tables"三连操作——不是怕漏数据,而是怕漏掉那个让回测结果漂移2%的adj_factor精度丢失。希望帮到你。
本文还有配套的精品资源,点击获取