ZhuLinsen/daily_stock_analysis · error · DataFetchError

Akshare 获取港股数据失败: {e}

Error message

Akshare 获取港股数据失败: {e}

What it means

The generic DataFetchError raised by AkshareFetcher._fetch_hk_data when ak.stock_hk_hist fails with any exception not classified as rate limiting. It wraps the original akshare exception (available via __cause__) and is the signal for DataFetcherManager to try the next HK data source in its priority chain.

Source

Thrown at data_provider/akshare_fetcher.py:885

            if df is not None and not df.empty:
                logger.info(f"[API返回] ak.stock_hk_hist 成功: 返回 {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.stock_hk_hist 返回空数据, 耗时 {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 获取港股数据失败: {e}") from e
    
    def _normalize_data(self, df: pd.DataFrame, stock_code: str) -> pd.DataFrame:
        """
        标准化 Akshare 数据
        
        Akshare 返回的列名(中文):
        日期, 开盘, 收盘, 最高, 最低, 成交量, 成交额, 振幅, 涨跌幅, 涨跌额, 换手率
        
        需要映射到标准列名:
        date, open, high, low, close, volume, amount, pct_chg
        """
        df = df.copy()
        
        # 列名映射(Akshare 中文列名 -> 标准英文列名)
        column_mapping = {
            '日期': 'date',
            '开盘': 'open',
            '收盘': 'close',

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Check e.__cause__: TypeError usually means akshare API change → pin/upgrade akshare; JSONDecodeError → block page or endpoint change.
  2. Normalize HK codes to 5 digits before calling (e.g. '00700', not '700').
  3. Retry once after a short sleep for transient 5xx, then let fallback take over.
  4. Confirm ak.stock_hk_hist works standalone in a REPL with the same arguments.

Example fix

# before
akshare_fetcher.fetch('700', start, end)  # HK code missing padding

# after
code = '700'.zfill(5)  # '00700'
akshare_fetcher.fetch(code, start, end)
Defensive patterns

Strategy: fallback

Validate before calling

code = '700'.zfill(5)  # HK codes need 5-digit zero padding for ak.stock_hk_hist
assert len(code) == 5 and code.isdigit()

Type guard

def is_valid_hk_code(code: str) -> bool:
    c = code.lower().removeprefix('hk')
    return len(c) == 5 and c.isdigit()

Try / catch

try:
    df = akshare_fetcher.fetch(hk_code, start, end)
except RateLimitError:
    raise
except DataFetchError as e:
    logger.warning('akshare HK failed for %s: %s', hk_code, e)
    df = manager.fetch(hk_code, start, end)

Prevention

When it happens

Trigger: Calling fetch for an HK code ('00700') when stock_hk_hist raises: invalid symbol argument (e.g. '700' without zero padding), network timeout to East Money, JSON decode error on an HTML block page without keywords, or akshare interface changes after upgrade.

Common situations: HK codes passed without 5-digit zero-padding; akshare version drift changing stock_hk_hist parameters ('period'/'adjust' renamed); intermittent East Money 5xx errors.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/09a9d05664b4ee44. Report an issue: GitHub.