84 lines
3.0 KiB
Python
84 lines
3.0 KiB
Python
"""
|
|
龙虎榜采集器
|
|
主方案:AkShare stock_lhb_detail_daily_sina(已实测可用,含上榜原因)
|
|
备选:东财 datacenter 接口(报表名不确定,暂时注释备用)
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
|
|
import akshare as ak
|
|
import pandas as pd
|
|
from datetime import datetime, timedelta
|
|
from src.storage.db import upsert_df, upsert_log, get_last_fetch
|
|
|
|
|
|
def fetch_lhb_date(trade_date: str) -> pd.DataFrame:
|
|
"""
|
|
获取单日龙虎榜
|
|
trade_date: "YYYY-MM-DD" 或 "YYYYMMDD"
|
|
"""
|
|
# 统一为 YYYYMMDD 格式
|
|
ds = trade_date.replace("-", "")
|
|
try:
|
|
df = ak.stock_lhb_detail_daily_sina(date=ds)
|
|
except Exception as e:
|
|
print(f" [WARN] LHB Sina failed for {trade_date}: {e}")
|
|
return pd.DataFrame()
|
|
|
|
if df is None or df.empty:
|
|
return pd.DataFrame()
|
|
|
|
# Sina 返回列(实测):序号, 股票代码, 股票名称, 收盘价, 涨跌幅, 成交额, 成交额, 上榜原因
|
|
# 编码问题导致中文列名显示乱码,用位置索引映射
|
|
# 0=序号, 1=代码, 2=名称, 3=收盘, 4=涨跌幅, 5=成交额, 6=??? 7=上榜原因
|
|
df.columns = ["no", "ts_code", "name", "close", "pct_change", "amount", "amount2", "reason"]
|
|
|
|
df["trade_date"] = trade_date.replace("-", "")
|
|
# 格式化为 YYYY-MM-DD
|
|
df["trade_date"] = pd.to_datetime(df["trade_date"]).dt.strftime("%Y-%m-%d")
|
|
|
|
# 保留有用字段
|
|
keep = ["trade_date", "ts_code", "name", "close", "pct_change", "amount", "reason"]
|
|
df = df[[c for c in keep if c in df.columns]]
|
|
return df
|
|
|
|
|
|
def fetch_lhb_range(start_date: str, end_date: str = None) -> pd.DataFrame:
|
|
"""抓取日期区间的龙虎榜(逐日)"""
|
|
end_date = end_date or datetime.today().strftime("%Y-%m-%d")
|
|
all_days = []
|
|
cur = datetime.strptime(start_date.replace("-", ""), "%Y%m%d")
|
|
end = datetime.strptime(end_date.replace("-", ""), "%Y%m%d")
|
|
while cur <= end:
|
|
ds = cur.strftime("%Y-%m-%d")
|
|
df = fetch_lhb_date(ds)
|
|
if df is not None and not df.empty:
|
|
all_days.append(df)
|
|
cur += timedelta(days=1)
|
|
if all_days:
|
|
return pd.concat(all_days, ignore_index=True)
|
|
return pd.DataFrame()
|
|
|
|
|
|
def incremental_lhb(source: str = "lhb_daily") -> pd.DataFrame:
|
|
"""增量采集:只抓上次之后的新交易日"""
|
|
last = get_last_fetch(source)
|
|
start = last.get("last_date") or (datetime.today() - timedelta(days=7)).strftime("%Y-%m-%d")
|
|
end = datetime.today().strftime("%Y-%m-%d")
|
|
df = fetch_lhb_range(start, end)
|
|
if df is not None and not df.empty:
|
|
upsert_df(df, "lhb_daily")
|
|
upsert_log(source, param="", last_date=end)
|
|
return df
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import fire
|
|
fire.Fire({
|
|
"date": lambda d: upsert_df(fetch_lhb_date(d), "lhb_daily"),
|
|
"range": lambda s, e=None: upsert_df(fetch_lhb_range(s, e), "lhb_daily"),
|
|
"incr": lambda: incremental_lhb(),
|
|
})
|