ZhuLinsen/daily_stock_analysis · error · RateLimitError
Akshare(EM) 可能被限流: {e}
Error message
Akshare(EM) 可能被限流: {e} What it means
RateLimitError raised by _fetch_stock_data_em when ak.stock_zh_a_hist fails and the lowercased error text contains one of the anti-scraping keywords ('banned', 'blocked', '频率', 'rate', '限制'). It is a heuristic classification: the source (Eastmoney) probably throttled or blocked the client, and the fetcher marks it so upstream logic can back off or switch channels instead of hammering.
Source
Thrown at data_provider/akshare_fetcher.py:569
period="daily",
start_date=start_date.replace('-', ''),
end_date=end_date.replace('-', ''),
adjust="qfq"
)
api_elapsed = _time.time() - api_start
if df is not None and not df.empty:
logger.info(f"[API返回] ak.stock_zh_a_hist 成功: {len(df)} 行, 耗时 {api_elapsed:.2f}s")
return df
else:
logger.warning(f"[API返回] ak.stock_zh_a_hist 返回空数据")
return pd.DataFrame()
except Exception as e:
error_msg = str(e).lower()
if any(keyword in error_msg for keyword in ['banned', 'blocked', '频率', 'rate', '限制']):
raise RateLimitError(f"Akshare(EM) 可能被限流: {e}") from e
raise e
def _fetch_stock_data_sina(self, stock_code: str, start_date: str, end_date: str) -> pd.DataFrame:
"""
获取普通 A 股历史数据 (新浪财经)
数据来源:ak.stock_zh_a_daily()
"""
import akshare as ak
# 转换代码格式:sh600000, sz000001, bj920748
symbol = _to_sina_tx_symbol(stock_code)
self._enforce_rate_limit()
try:
df = _akshare_call_with_timeout(
ak.stock_zh_a_daily,
symbol=symbol,View on GitHub (pinned to 5159bd72e8)
Solutions
- Back off: wait minutes (not seconds) before retrying this channel; rate-limit windows usually expire.
- Increase inter-call sleep/randomization in the fetch strategy and reduce batch size.
- Catch RateLimitError and switch to the Sina channel or yfinance for the remainder of the run.
- Run the job from a different egress IP or off-peak hours.
- Persist fetched data to cache so repeat runs don't re-hit the API.
Example fix
# before
for code in codes:
df = fetcher.fetch_stock_data(code, start, end)
# after: catch RateLimitError and back off / reroute
from data_provider.exceptions import RateLimitError
for code in codes:
try:
df = fetcher.fetch_stock_data(code, start, end)
except RateLimitError:
df = alternate_fetcher.fetch_stock_data(code, start, end) # sina/yfinance
time.sleep(30) Defensive patterns
Strategy: retry
Type guard
from data_provider.exceptions import RateLimitError
def is_akshare_rate_limited(exc: Exception) -> bool:
return isinstance(exc, RateLimitError) Try / catch
try:
df = fetcher._fetch_stock_data_em(code, start, end)
except RateLimitError:
time.sleep(300) # throttle windows are minutes, not seconds
df = fetcher._fetch_stock_data_sina(code, start, end) # or switch channel/source Prevention
- Rate-limit your own batch jobs (spacing, jitter) rather than relying on ban detection.
- Cache daily data so repeat analyses never re-hit Eastmoney within the same day.
- Treat RateLimitError as a signal to slow the whole batch, not just the failing symbol.
When it happens
Trigger: Calling the A-share Eastmoney channel repeatedly in a short window; the exception message from akshare/requests contains e.g. '请求频率' or 'blocked'; triggering protection after sustained scraping from one IP.
Common situations: Scheduled analysis jobs running many symbols with short intervals; shared office/NAT egress IPs already flagged by Eastmoney; debugging loops that re-fetch the same symbol dozens of times.
Related errors
- Akshare 可能被限流: {e}
- Akshare 所有渠道获取失败: {last_error}
- {call_name} 调用超过 {wait_seconds:g}s,已放弃等待
- {call_name} 调用进程未返回结果
- AkshareFetcher 不支持美股 {stock_code},请使用 YfinanceFetcher 获取正确的复
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/a044d3cd59d9bd77.
Report an issue: GitHub.