"""东方财富 CHOICE API 利润表抓取工具。 - 输入: 股票代码列表 (如 ['603233.SH']) - 输出: pandas DataFrame (按报告期的利润表数据) - 不需要登录, 不需要 cookie, 无文件落盘 """ from __future__ import annotations import pandas as pd from utils._emchoice import get_data_url, fetch_report _PATH = "/stock/f9/CWSJ_LRB" # 默认报告期类型: 1=一季报, 3=三季报, 5=中报, 6=年报 (注意: 该接口需带引号) _DEFAULT_REPORT_PERIOD_TYPE = '"1","5","3","6","7"' def _build_params( secucode: str, start_date: str, end_date: str, report_period_type: str, ) -> dict[str, str]: """构造 CWSJ_LRB 请求参数 (其余参数沿用 docs/api_docs.md 抓包值).""" return { "SecurityCode": secucode, "StartDate": start_date, "EndDate": end_date, "ReportPeriodType": report_period_type, "ReportShowForm": "0", "Digits": "2", "Sort": "-1", "ListedType": '"0","1"', "IsShowEmptyRow": "true", "ExchangeRateType": "1", "Currency": "CNY:1", "Magnitude": "4", "ReportType": '"1"', } def _fetch_one( secucode: str, years: int, report_period_type: str, ) -> pd.DataFrame: """拉一只股票的利润表.""" today = pd.Timestamp.today() start = (today - pd.DateOffset(years=years)).strftime("%Y-%m-%d") end = today.strftime("%Y-%m-%d") params = _build_params(secucode, start, end, report_period_type) data_url = get_data_url(_PATH, params) rows = fetch_report(data_url) df = pd.DataFrame(rows) if not df.empty: df["SECUCODE"] = secucode return df def get_income( secucodes: list[str], years: int = 3, report_period_type: str = _DEFAULT_REPORT_PERIOD_TYPE, ) -> pd.DataFrame: """抓取指定股票的利润表并按股票合并。""" codes = [code.strip() for code in secucodes if code.strip()] if not codes: return pd.DataFrame() frames: list[pd.DataFrame] = [] for code in codes: df = _fetch_one(code, years, report_period_type) if not df.empty: frames.append(df) if not frames: return pd.DataFrame() return pd.concat(frames, ignore_index=True)