1 数据获取
本章介绍如何使用Python和akshare获取金融数据,涵盖股票行情、市场指数、宏观经济指标和财务指标四类数据。
1.1 数据源选择
本次作业选择 akshare 作为数据源,原因如下:
注意akshare优势
- 免费:无需注册,无需付费,开箱即用
- 覆盖广:涵盖股票、指数、基金、期货、外汇等多种金融数据
- 接口简单:Python API友好,便于程序化获取
- 社区活跃:持续更新,文档完善
其他可选数据源对比:
| 工具 | 特点 | 参考接口 |
|---|---|---|
akshare |
免费,无需注册,覆盖广 | ak.stock_zh_a_hist() |
baostock |
免费,需登录,数据稳定 | bs.query_history_k_data_plus() |
tushare |
需注册获取token,数据丰富 | pro.daily() |
1.2 环境准备
1.2.1 导入库
# 导入必要的库
import akshare as ak
import pandas as pd
import numpy as np
from datetime import datetime
import os
import logging
# 打印版本信息
print(f"akshare版本: {ak.__version__}")
print(f"pandas版本: {pd.__version__}")1.2.2 创建目录结构
使用Python代码自动创建项目目录结构:
# 创建数据目录
directories = [
'data/stock',
'data/index',
'data/macro',
'data/finance',
'data/clean',
'data/combined',
'output'
]
for d in directories:
os.makedirs(d, exist_ok=True)
print(f"创建目录: {d}")1.3 股票数据下载
1.3.1 股票列表定义
选择覆盖7个行业的10只A股股票:
stock_list = [
{"code": "600036", "name": "招商银行", "industry": "银行"},
{"code": "601398", "name": "工商银行", "industry": "银行"},
{"code": "002594", "name": "比亚迪", "industry": "汽车"},
{"code": "600104", "name": "上汽集团", "industry": "汽车"},
{"code": "000002", "name": "万科A", "industry": "房地产"},
{"code": "600519", "name": "贵州茅台", "industry": "白酒"},
{"code": "000858", "name": "五粮液", "industry": "白酒"},
{"code": "601857", "name": "中国石油", "industry": "能源"},
{"code": "000063", "name": "中兴通讯", "industry": "通讯"},
{"code": "002352", "name": "顺丰控股", "industry": "物流"},
]
提示选股原则
- 覆盖至少5个行业(实际覆盖7个)
- 每个行业至多2只股票
- 优先选择行业龙头,确保代表性和流动性
1.3.2 下载函数
定义带日志记录的下载函数:
def download_stock_data(stock_code, stock_name, start_date, end_date, log_file):
"""下载单只股票的后复权日度行情数据"""
try:
df = ak.stock_zh_a_hist(
symbol=stock_code,
period="daily",
start_date=start_date,
end_date=end_date,
adjust="hfq" # 后复权
)
# 记录成功日志
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(log_file, 'a', encoding='utf-8') as f:
f.write(f"[{timestamp}] SUCCESS stock_{stock_code} shape={df.shape}\n")
return df
except Exception as e:
# 记录失败日志
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(log_file, 'a', encoding='utf-8') as f:
f.write(f"[{timestamp}] FAILED stock_{stock_code} Error: {str(e)}\n")
return None1.3.3 批量下载
start_date = "20200101"
end_date = datetime.now().strftime("%Y%m%d")
log_file = "download_log.txt"
for stock in stock_list:
df = download_stock_data(
stock['code'],
stock['name'],
start_date,
end_date,
log_file
)
if df is not None:
# 保存到CSV
df.to_csv(f"data/stock/stock_{stock['code']}.csv", index=False)
print(f"{stock['name']}: 下载成功,{len(df)}条记录")1.3.4 下载结果
| 股票 | 代码 | 数据行数 | 状态 |
|---|---|---|---|
| 招商银行 | 600036 | 1515 | ✓ |
| 工商银行 | 601398 | 1515 | ✓ |
| 比亚迪 | 002594 | 1515 | ✓ |
| 上汽集团 | 600104 | 1515 | ✓ |
| 万科A | 000002 | 1515 | ✓ |
| 贵州茅台 | 600519 | 1515 | ✓ |
| 五粮液 | 000858 | 1515 | ✓ |
| 中国石油 | 601857 | 1515 | ✓ |
| 中兴通讯 | 000063 | 1514 | ✓ |
| 顺丰控股 | 002352 | 1512 | ✓ |
1.4 指数数据下载
下载沪深300和中证500指数数据:
# 沪深300作为CAPM市场基准
hs300 = ak.stock_zh_index_daily(symbol="sh000300")
hs300.to_csv("data/index/index_000300.csv", index=False)
print(f"沪深300: {len(hs300)}条记录")
# 中证500代表中小盘股票
zz500 = ak.stock_zh_index_daily(symbol="sh000905")
zz500.to_csv("data/index/index_000905.csv", index=False)
print(f"中证500: {len(zz500)}条记录")1.4.1 指数选择说明
| 指数 | 代码 | 用途 |
|---|---|---|
| 沪深300 | 000300 | CAPM模型市场基准,代表A股大盘股 |
| 中证500 | 000905 | 代表A股中小盘股票,与沪深300形成互补 |
1.5 宏观数据下载
1.5.1 CPI同比增速
# 下载CPI同比增速
cpi = ak.macro_china_cpi_yearly()
cpi.to_csv("data/macro/macro_cpi.csv", index=False)
print(f"CPI数据: {len(cpi)}条记录")
注意CPI与股市的关系
CPI同比增速反映通货膨胀水平:
- 高通胀:可能导致货币政策收紧,对股市形成压力
- 低通胀:通常有利于股市表现
- 行业差异:不同行业对通胀的敏感度不同
1.5.2 M2同比增速
# 下载M2同比增速
m2 = ak.macro_china_m2_yearly()
m2.to_csv("data/macro/macro_m2.csv", index=False)
print(f"M2数据: {len(m2)}条记录")M2同比增速反映货币供应量变化,与股市流动性密切相关。货币供应增加通常利好股市。
1.6 财务数据下载
1.6.1 获取财务指标
# 从财务摘要中提取最近5个年度的 ROE 与净利润率
indicator_map = {
"净资产收益率(ROE)": "ROE",
"销售净利率": "净利润率"
}
rows = []
for stock in stock_list:
code = stock["code"]
try:
df = ak.stock_financial_abstract(symbol=code)
# 仅保留“常用指标”分组,避免同名指标重复
df = df[df["选项"] == "常用指标"].copy()
# 年报列:YYYY1231
annual_cols = sorted(
[c for c in df.columns if str(c).isdigit() and str(c).endswith("1231")],
reverse=True
)
# 每只股票取“最新可得”的5个年报年度(避免个别股票缺少最新年)
latest_annual_cols = annual_cols[:5]
for raw_name, std_name in indicator_map.items():
sub = df[df["指标"] == raw_name]
if sub.empty:
continue
s = sub.iloc[0]
for c in latest_annual_cols:
year = int(str(c)[:4])
value = pd.to_numeric(s[c], errors="coerce")
if pd.notna(value):
rows.append({
"code": code,
"year": year,
"indicator": std_name,
"value": float(value)
})
except Exception as e:
print(f"{stock['name']}: 财务数据获取失败 - {e}")
finance_df = pd.DataFrame(rows)
finance_df = finance_df.drop_duplicates(["code", "year", "indicator"])
finance_df.to_csv("data/finance/finance_ratios.csv", index=False)1.6.2 财务数据格式
财务指标整理为长格式(Long format):
| 字段 | 说明 |
|---|---|
| code | 股票代码 |
| year | 年度 |
| indicator | 指标名称 |
| value | 指标值 |
1.7 下载日志
所有下载操作均记录到 download_log.txt 文件中:
[2026-04-07 22:23:02] SUCCESS stock_600036 shape=(1515, 7)
[2026-04-07 22:23:03] SUCCESS stock_601398 shape=(1515, 7)
[2026-04-07 22:23:05] SUCCESS stock_002594 shape=(1515, 7)
...
日志格式说明:
| 字段 | 示例 | 说明 |
|---|---|---|
| 时间戳 | 2026-04-07 22:23:02 | 下载时间 |
| 状态 | SUCCESS/FAILED | 下载结果 |
| 数据标识 | stock_600036 | 数据类型和代码 |
| 详情 | shape=(1515, 7) | 数据维度或错误信息 |
1.8 小结
本章完成了以下数据获取工作:
| 数据类型 | 数量 | 来源 | 时间范围 |
|---|---|---|---|
| 股票行情 | 10只 | akshare | 2020-01-01至今 |
| 市场指数 | 2只 | akshare | 同上 |
| 宏观指标 | 2项 | akshare | 月度数据 |
| 财务指标 | 2类 | akshare | 年度数据 |
警告注意事项
- akshare接口可能因服务器维护而暂时不可用
- 部分股票可能因停牌等原因数据不完整
- 下载日志是问题排查的重要依据
下一章将介绍数据清洗工作。