1. 为什么需要批量获取股票日线数据
在金融数据分析领域,获取完整的股票日线数据是量化交易、策略回测和基本面分析的基础工作。传统的手动下载方式存在几个明显痛点:
- 数据分散:不同交易所、不同板块的股票数据分布在多个数据源
- 格式混乱:CSV、Excel、JSON等各种格式混杂
- 更新滞后:手动操作无法保证数据的及时性
- 规模限制:单次下载往往有数量限制
我曾在某私募基金负责数据采集工作,最初尝试用Excel手动下载,不仅效率低下(完整获取A股4000多只股票数据需要3天),还经常因网络波动导致前功尽弃。后来转向Python自动化方案,将获取时间缩短到20分钟以内。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据源选择与API比较
2.1 主流免费数据源对比
| 数据源 | 覆盖范围 | 频率限制 | 历史深度 | 数据字段 | 稳定性 |
|---|---|---|---|---|---|
| AKShare | 全球多市场 | 无明确限制 | 10年+ | OHLCV+指标 | ★★★★☆ |
| Tushare Pro | 主要中概股 | 500次/日 | 5年 | 基础行情 | ★★★☆☆ |
| Yahoo Finance | 全球上市公司 | IP限制 | 不固定 | OHLCV | ★★☆☆☆ |
| Alpha Vantage | 美股为主 | 5次/分钟 | 20年+ | 多时间粒度 | ★★★☆☆ |
提示:新手建议从AKShare开始,它提供最完整的中文文档和示例代码,且无需注册即可使用基础功能。
2.2 付费数据源考量
对于机构用户,Wind和同花顺i问财提供更专业的服务:
- 数据清洗更彻底(除权除息自动处理)
- 包含财务数据和特色指标
- 支持实时推送
- 但年费通常在2万元以上
3. 环境配置与依赖安装
3.1 基础环境要求
bash复制# 创建专用虚拟环境(推荐Python 3.8+)
python -m venv stock_env
source stock_env/bin/activate # Linux/Mac
stock_env\Scripts\activate # Windows
# 安装核心依赖
pip install akshare pandas numpy requests cachetools
3.2 常见安装问题解决
- SSL证书错误:
python复制import ssl
ssl._create_default_https_context = ssl._create_unverified_context
- 速度优化配置:
python复制import akshare as ak
ak.set_proxy(proxy="http://代理IP:端口") # 国内访问国际数据源时需要
- 数据缓存机制:
python复制from cachetools import cached, TTLCache
cache = TTLCache(maxsize=1000, ttl=3600)
@cached(cache)
def get_stock_data(stock_code):
return ak.stock_zh_a_daily(symbol=stock_code)
4. 核心代码实现与优化
4.1 基础获取函数
python复制import akshare as ak
import pandas as pd
from tqdm import tqdm
def fetch_single_stock(stock_code, start_date="19900101"):
"""
获取单只股票日线数据
:param stock_code: 股票代码(带交易所前缀,如sh600000)
:param start_date: 开始日期(YYYYMMDD)
:return: DataFrame
"""
try:
df = ak.stock_zh_a_daily(symbol=stock_code, start_date=start_date)
df['code'] = stock_code
return df
except Exception as e:
print(f"获取{stock_code}失败: {str(e)}")
return pd.DataFrame()
4.2 批量获取优化方案
版本1:基础循环(效率低)
python复制def fetch_all_stocks_basic(stock_list):
all_data = []
for code in stock_list:
df = fetch_single_stock(code)
all_data.append(df)
return pd.concat(all_data)
版本2:多线程加速
python复制from concurrent.futures import ThreadPoolExecutor
def fetch_all_stocks_parallel(stock_list, workers=8):
with ThreadPoolExecutor(max_workers=workers) as executor:
results = list(tqdm(executor.map(fetch_single_stock, stock_list),
total=len(stock_list)))
return pd.concat([r for r in results if not r.empty])
版本3:断点续传
python复制import os
def fetch_with_resume(stock_list, output_dir="stock_data"):
os.makedirs(output_dir, exist_ok=True)
existing = [f.split('.')[0] for f in os.listdir(output_dir)]
todo_list = [c for c in stock_list if c not in existing]
for code in tqdm(todo_list):
df = fetch_single_stock(code)
if not df.empty:
df.to_csv(f"{output_dir}/{code}.csv")
5. 数据清洗与存储方案
5.1 常见数据问题处理
python复制def clean_data(raw_df):
# 处理异常值
df = raw_df.copy()
df = df[df['volume'] > 0] # 剔除零成交量日
# 处理涨跌幅限制
df['pct_chg'] = df['pct_chg'].clip(-10.5, 10.5)
# 前复权处理
df['close_adj'] = df['close'] * df['factor']
df['high_adj'] = df['high'] * df['factor']
df['low_adj'] = df['low'] * df['factor']
df['open_adj'] = df['open'] * df['factor']
return df
5.2 存储方案对比
| 存储方式 | 写入速度 | 查询效率 | 空间占用 | 适用场景 |
|---|---|---|---|---|
| CSV | 快 | 慢 | 中 | 小型数据集 |
| Parquet | 中 | 快 | 小 | 中型数据集 |
| SQLite | 慢 | 快 | 中 | 频繁查询 |
| MySQL | 中 | 快 | 大 | 多用户协作 |
| HDF5 | 快 | 快 | 小 | 超大规模数据 |
Parquet存储示例:
python复制def save_to_parquet(df, filename):
df.to_parquet(
filename,
engine='pyarrow',
compression='snappy',
index=False
)
6. 实战案例:构建完整数据管道
6.1 获取股票列表
python复制def get_all_stock_codes():
# 获取沪深京A股列表
stock_zh = ak.stock_zh_a_spot()
sh_stocks = ['sh' + c for c in stock_zh[stock_zh['code'].str.startswith('6')]['code']]
sz_stocks = ['sz' + c for c in stock_zh[stock_zh['code'].str.startswith(('0', '3'))]['code']]
bj_stocks = ['bj' + c for c in stock_zh[stock_zh['code'].str.startswith('8')]['code']]
return sh_stocks + sz_stocks + bj_stocks
6.2 完整执行流程
python复制if __name__ == "__main__":
# 步骤1:获取股票列表
all_codes = get_all_stock_codes()
print(f"共获取到{len(all_codes)}只股票")
# 步骤2:分批获取(每批200只)
batch_size = 200
for i in range(0, len(all_codes), batch_size):
batch = all_codes[i:i+batch_size]
print(f"正在处理第{i//batch_size+1}批,共{len(batch)}只股票")
# 步骤3:并行获取数据
data = fetch_all_stocks_parallel(batch)
# 步骤4:数据清洗
cleaned = clean_data(data)
# 步骤5:存储
save_to_parquet(cleaned, f"stock_data_batch_{i//batch_size+1}.parquet")
# 步骤6:间隔避免被封
time.sleep(10)
7. 性能优化技巧
- 请求间隔控制:
python复制import random
import time
def safe_request():
time.sleep(random.uniform(0.5, 1.5)) # 随机间隔
- 数据压缩传输:
python复制session = requests.Session()
session.headers.update({'Accept-Encoding': 'gzip, deflate'})
- 异常重试机制:
python复制from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
def fetch_with_retry(code):
return fetch_single_stock(code)
- 内存优化:
python复制dtypes = {
'open': 'float32',
'high': 'float32',
'low': 'float32',
'close': 'float32',
'volume': 'int32'
}
df = df.astype(dtypes)
8. 常见问题解决方案
问题1:获取的数据缺失最近交易日
- 检查数据源是否更新
- 确认系统时区设置为东八区
- 尝试清除缓存重新获取
问题2:报错ConnectionResetError
- 降低并发数量(将workers从8降到4)
- 添加代理设置
- 使用try-catch包裹请求代码
问题3:数据突然停止更新
- 检查API调用次数是否超限
- 查看数据源公告是否维护
- 验证账户权限是否有效
问题4:获取的数据顺序混乱
python复制# 确保按日期排序
df = df.sort_values('date').reset_index(drop=True)
9. 数据质量验证方法
- 基础统计检查:
python复制def basic_checks(df):
# 检查日期连续性
date_diff = pd.to_datetime(df['date']).diff().dt.days
assert all(date_diff[1:] <= 5), "存在日期跳空"
# 检查价格合理性
assert (df['high'] >= df['low']).all(), "最高价低于最低价"
assert (df['high'] >= df['close']).all(), "最高价低于收盘价"
assert (df['high'] >= df['open']).all(), "最高价低于开盘价"
- 横向对比验证:
python复制def cross_check(sample_codes):
dfs = []
for code in sample_codes:
df = fetch_single_stock(code)
dfs.append(df.groupby('date')['volume'].sum())
corr_matrix = pd.concat(dfs, axis=1).corr()
assert corr_matrix.mean().mean() > 0.7, "个股成交量相关性过低"
- 与基准指数对比:
python复制def index_correlation_check(stock_df, index_code='sh000001'):
index_df = fetch_single_stock(index_code)
merged = pd.merge(stock_df, index_df, on='date', suffixes=('_stock', '_index'))
corr = merged['pct_chg_stock'].corr(merged['pct_chg_index'])
assert abs(corr) > 0.5, "与大盘相关性异常"
10. 数据更新维护方案
10.1 增量更新策略
python复制def incremental_update(existing_file, new_file):
old_df = pd.read_parquet(existing_file)
latest_date = old_df['date'].max()
new_df = pd.read_parquet(new_file)
new_df = new_df[new_df['date'] > latest_date]
updated = pd.concat([old_df, new_df])
updated.to_parquet(existing_file)
10.2 自动化调度方案
Linux crontab示例:
bash复制0 18 * * 1-5 /path/to/python /path/to/update_script.py >> /var/log/stock_update.log 2>&1
Windows任务计划程序:
- 创建基本任务
- 触发器设置为"工作日每天18:00"
- 操作为"启动程序":
python.exe update_script.py - 添加参数和工作目录
10.3 监控告警配置
python复制import smtplib
from email.mime.text import MIMEText
def send_alert(subject, content):
msg = MIMEText(content)
msg['Subject'] = subject
msg['From'] = 'sender@example.com'
msg['To'] = 'receiver@example.com'
with smtplib.SMTP('smtp.example.com', 587) as server:
server.login('user', 'password')
server.send_message(msg)
