ZhuLinsen/daily_stock_analysis · error · DataFetchError
Akshare 获取 ETF 数据失败: {e}
Error message
Akshare 获取 ETF 数据失败: {e} What it means
Raised by AkshareFetcher._fetch_etf_data when ak.fund_etf_hist_em throws an exception that does not match the rate-limit keyword list ('banned', 'blocked', '频率', 'rate', '限制'). It is a DataFetchError wrapping the original akshare exception, so the DataFetcherManager fallback chain can catch it and move to the next data source. The keyword check runs first, so genuine anti-crawler bans surface as RateLimitError instead of this error.
Source
Thrown at data_provider/akshare_fetcher.py:726
if df is not None and not df.empty:
logger.info(f"[API返回] ak.fund_etf_hist_em 成功: 返回 {len(df)} 行数据, 耗时 {api_elapsed:.2f}s")
logger.info(f"[API返回] 列名: {list(df.columns)}")
logger.info(f"[API返回] 日期范围: {df['日期'].iloc[0]} ~ {df['日期'].iloc[-1]}")
logger.debug(f"[API返回] 最新3条数据:\n{df.tail(3).to_string()}")
else:
logger.warning(f"[API返回] ak.fund_etf_hist_em 返回空数据, 耗时 {api_elapsed:.2f}s")
return df
except Exception as e:
error_msg = str(e).lower()
# 检测反爬封禁
if any(keyword in error_msg for keyword in ['banned', 'blocked', '频率', 'rate', '限制']):
logger.warning(f"检测到可能被封禁: {e}")
raise RateLimitError(f"Akshare 可能被限流: {e}") from e
raise DataFetchError(f"Akshare 获取 ETF 数据失败: {e}") from e
def _fetch_us_data(self, stock_code: str, start_date: str, end_date: str) -> pd.DataFrame:
"""
获取美股历史数据
数据来源:ak.stock_us_daily()(新浪财经接口)
Args:
stock_code: 美股代码,如 'AMD', 'AAPL', 'TSLA'
start_date: 开始日期,格式 'YYYY-MM-DD'
end_date: 结束日期,格式 'YYYY-MM-DD'
Returns:
美股历史数据 DataFrame
"""
import akshare as ak
# 防封禁策略 1: 随机 User-AgentView on GitHub (pinned to 5159bd72e8)
Solutions
- Inspect the wrapped cause (e.__cause__) in logs — the akshare exception text identifies the real failure (timeout vs TypeError vs JSONDecodeError).
- If it is a TypeError/AttributeError, pin or upgrade akshare to the version the fetcher was written against and retest with `ak.fund_etf_hist_em` in a REPL.
- If it is network-related, verify connectivity to eastmoney.com and increase the request timeout / retry via the fetcher's base options.
- Let DataFetcherManager fall through to the next configured source for ETF data rather than retrying akshare immediately.
Example fix
// before
try:
df = fetcher.fetch('510300', '2025-01-01', '2025-06-30')
except DataFetchError as e:
print(e) # cause lost in print
// after
import traceback
try:
df = fetcher.fetch('510300', '2025-01-01', '2025-06-30')
except DataFetchError as e:
logging.error('ETF fetch failed: %s | cause: %s', e, e.__cause__)
df = manager.fetch('510300', ...) # fallback chain Defensive patterns
Strategy: fallback
Validate before calling
import akshare as ak # smoke-test the ETF interface before a batch run df = ak.fund_etf_hist_em(symbol='510300', period='daily', start_date='20250101', end_date='20250630') assert not df.empty
Type guard
def is_akshare_etf_available() -> bool:
try:
import akshare as ak
return callable(getattr(ak, 'fund_etf_hist_em', None))
except ImportError:
return False Try / catch
try:
df = akshare_fetcher.fetch(etf_code, start, end)
except RateLimitError:
raise # let rate-limit policy handle it
except DataFetchError as e:
logger.warning('akshare ETF failed (%s); falling back', e)
df = manager.fetch(etf_code, start, end) Prevention
- Smoke-test akshare interfaces after every akshare upgrade — they change frequently.
- Keep DataFetcherManager fallback chains configured for ETF codes so one source failing never kills the run.
- Log e.__cause__ alongside the wrapper message for diagnosable failures.
When it happens
Trigger: Calling fetch on an ETF code (e.g. '510300') where ak.fund_etf_hist_em raises a non-rate-limit exception: network timeout, missing akshare optional dependency, akshare interface signature change after upgrade, or an East Money endpoint returning malformed JSON. Any exception whose lowercased message lacks the ban keywords lands here.
Common situations: akshare piped to a version where fund_etf_hist_em renamed arguments; proxy/DNS failures in CI; East Money occasionally returning HTML error pages that break JSON parsing; running on a network where eastmoney.com is unreachable.
Related errors
- {call_name} 调用超过 {wait_seconds:g}s,已放弃等待
- Akshare 所有渠道获取失败: {last_error}
- Akshare 可能被限流: {e}
- Akshare 获取美股数据失败: {e}
- Akshare 获取港股数据失败: {e}
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/48d29e891150dd36.
Report an issue: GitHub.