{"record":{"id":"164bb9c743197666","repo":"ZhuLinsen/daily_stock_analysis","slug":"akshare-last-error","errorCode":null,"errorMessage":"Akshare 所有渠道获取失败: {last_error}","messagePattern":"Akshare 所有渠道获取失败: (.+?)","errorType":"exception","errorClass":"DataFetchError","httpStatus":null,"severity":"error","filePath":"data_provider/akshare_fetcher.py","lineNumber":528,"sourceCode":"        ]\n\n        last_error = None\n\n        for fetch_method, source_name in methods:\n            try:\n                logger.info(f\"[数据源] 尝试使用 {source_name} 获取 {stock_code}...\")\n                df = fetch_method(stock_code, start_date, end_date)\n\n                if df is not None and not df.empty:\n                    logger.info(f\"[数据源] {source_name} 获取成功\")\n                    return df\n            except Exception as e:\n                last_error = e\n                logger.warning(f\"[数据源] {source_name} 获取失败: {e}\")\n                # 继续尝试下一个\n\n        # 所有都失败\n        raise DataFetchError(f\"Akshare 所有渠道获取失败: {last_error}\")\n\n    def _fetch_stock_data_em(self, stock_code: str, start_date: str, end_date: str) -> pd.DataFrame:\n        \"\"\"\n        获取普通 A 股历史数据 (东方财富)\n        数据来源：ak.stock_zh_a_hist()\n        \"\"\"\n        import akshare as ak\n\n        # 防封禁策略 1: 随机 User-Agent\n        self._set_random_user_agent()\n\n        # 防封禁策略 2: 强制休眠\n        self._enforce_rate_limit()\n\n        logger.info(f\"[API调用] ak.stock_zh_a_hist(symbol={stock_code}, ...)\")\n\n        try:\n            import time as _time","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/akshare_fetcher.py#L510-L546","documentation":"DataFetchError raised when all internal akshare channels for a fetch have been tried and every one failed; the last channel's exception text is embedded. In _fetch_stock_data the fallback order tries multiple sources (e.g. Eastmoney, Sina), logging a warning per failure, so this error means the whole chain is exhausted (often due to rate limiting or network problems affecting all sources).","triggerScenarios":"Fetching A-share daily data where both _fetch_stock_data_em (ak.stock_zh_a_hist) and _fetch_stock_data_sina (ak.stock_zh_a_daily) raise — e.g. the host is rate-limited/banned by both Eastmoney and Sina, or there is no egress to either host.","commonSituations":"Batch jobs fetching hundreds of symbols without pacing, triggering anti-scraping bans; running from a cloud IP range that data sources block; weekends/maintenance windows when endpoints return errors; outdated akshare whose endpoint signatures changed so every channel fails identically.","solutions":["Inspect last_error in the message — it tells you which failure mode dominated (rate limit vs network vs parsing).","If rate limiting: slow down (the fetcher already sleeps between calls), increase delays, or run off-hours.","Verify network access to eastmoney.com and sina.com from the host.","Upgrade akshare to match current upstream endpoints (pip install -U akshare).","Configure the provider fallback chain to try non-akshare sources (e.g. yfinance for overlap coverage) when this error occurs."],"exampleFix":"# before: hammering all symbols back-to-back\nfor code in codes:\n    df = fetcher.fetch_stock_data(code, start, end)\n\n# after: pace requests\nimport time\nfor code in codes:\n    df = fetcher.fetch_stock_data(code, start, end)\n    time.sleep(2)","handlingStrategy":"fallback","validationCode":"# pre-batch check: confirm at least one akshare channel responds before a long run\nprobe = fetcher.fetch_stock_data('000001', recent_5d_start, recent_5d_end)\nassert probe is not None and not probe.empty, 'akshare channels unhealthy; postpone batch'","typeGuard":"def is_akshare_all_channels_failed(exc: Exception) -> bool:\n    return isinstance(exc, DataFetchError) and '所有渠道获取失败' in str(exc)","tryCatchPattern":"try:\n    df = fetcher.fetch_stock_data(code, start, end)\nexcept DataFetchError as e:\n    if '所有渠道获取失败' in str(e):\n        log.warning('akshare exhausted for %s, falling back: %s', code, e)\n        df = alternate_fetcher.fetch_stock_data(code, start, end)\n    else:\n        raise","preventionTips":["Pace batch fetches (sleep + jitter) and cache results to avoid exhausting every channel at once.","Wire a second data source into the provider chain so exhaustion degrades instead of failing the run.","Alert when last_error mentions rate/ban keywords — the next full batch will fail too."],"tags":["akshare","rate-limit","fallback-exhausted","network","data-provider"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}