You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化yfinance批量获取标普500个股信息的运行性能

问题说明

需求为获取全部标普500成分股列表,拉取每只股票约170个字段的财务信息存入DataFrame,现有代码运行速度极慢,核心卡点为遍历单只股票时逐次发起独立API请求。原本预期yf.Tickers(sp500)可一次性批量拉取所有股票信息,但实际未达到预期效果。

原始代码
import pandas as pd
import yfinance as yf
import json
import datapackage
data_url = 'https://datahub.io/core/s-and-p-500-companies/datapackage.json'

# 加载Data Package
package = datapackage.Package(data_url)

# 仅加载表格类型数据
resources = package.resources
for resource in resources:
    if resource.tabular:
        data = pd.read_csv(resource.descriptor['path'])

spSymbols=data['Symbol']
sp500 =""
for i in spSymbols:
    sp500= sp500 + i +" " 
tickers= yf.Tickers(sp500)
dfStockInfo =[]
metricList=[]
# 性能瓶颈位置 ****
for i in spSymbols:
    stock = tickers.tickers.get(i)
    stockInfo= stock.info     
    metricList.append(stockInfo)
核心原因说明

yf.Tickers本质只是多标的的容器对象,初始化阶段不会预拉取任何标的的属性数据,只有访问单个标的的.info/.history等属性时,才会实时发起独立HTTP请求。逐次遍历500只股票串行请求,网络IO等待 占了总耗时的90%以上,是最核心的性能瓶颈,和逐个初始化yf.Ticker拉取数据没有效率差异。

优化方案

方案1:多线程并发请求(改动最小,适配全量info字段需求)

针对需要拉取全量170个info字段的场景,用多线程把串行的网络请求改成并发执行,把网络等待时间重叠,整体速度可提升5~10倍,注意控制并发数避免触发接口限流。

import pandas as pd
import yfinance as yf
from concurrent.futures import ThreadPoolExecutor, as_completed
import datapackage

# 拉取标普500成分股列表
data_url = 'https://datahub.io/core/s-and-p-500-companies/datapackage.json'
package = datapackage.Package(data_url)
sp500_df = None
for resource in package.resources:
    if resource.tabular:
        sp500_df = pd.read_csv(resource.descriptor['path'])
sp_symbols = sp500_df['Symbol'].tolist()

def fetch_single_stock_info(symbol: str) -> dict | None:
    try:
        # 加超时设置避免单只股票请求卡死
        ticker = yf.Ticker(symbol)
        info = ticker.info
        return info if info and "symbol" in info else None
    except Exception as e:
        print(f"拉取{symbol}数据失败: {str(e)}")
        return None

if __name__ == "__main__":
    metric_list = []
    # 并发数建议设置为10~20,过高容易触发雅虎接口限流封禁
    max_concurrent = 15
    with ThreadPoolExecutor(max_workers=max_concurrent) as executor:
        task_map = {executor.submit(fetch_single_stock_info, sym): sym for sym in sp_symbols}
        for task in as_completed(task_map):
            res = task.result()
            if res:
                metric_list.append(res)
    # 转换为DataFrame
    stock_info_df = pd.DataFrame(metric_list)

方案2:使用内置批量接口(适配非全量字段场景,速度最快)

如果不需要全量170个info字段,仅需要行情、估值、基础交易类指标,可直接使用yf.download内置批量接口,该接口本身为批量请求设计,仅需数次请求即可拉取全量标的数据,速度远高于逐次拉取info,但缺点是覆盖字段较少。

# 批量拉取标的行情数据,开启内置多线程
batch_price_data = yf.download(
    tickers=sp_symbols,
    period="1y",
    group_by="ticker",
    threads=True
)

额外优化建议

  • 增加本地缓存:可对yfinance的HTTP请求增加本地缓存,重复调试脚本时不需要重复拉取已请求过的数据,大幅降低调试等待时间
  • 持久化拉取结果:每次拉取完成后将DataFrame存储为本地parquet/csv文件,避免每次运行脚本都重新发起全量请求
  • 增加重试逻辑:针对网络波动导致的请求失败,可增加简单的重试机制,减少数据缺失

内容的提问来源于stack exchange,提问作者javadev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 13:06:30