更新BK缓存策略

This commit is contained in:
Sakurasan
2026-07-08 00:33:15 +08:00
parent 1a50f7680a
commit 015aeb989a
3 changed files with 55 additions and 22 deletions
+41 -16
View File
@@ -318,14 +318,14 @@ def _get_sector_session() -> AsyncSession:
global _sector_session
if _sector_session is None:
_sector_session = AsyncSession(
impersonate="chrome120",
impersonate="chrome131",
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Referer": "https://data.eastmoney.com/bkzj/hy.html",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
},
timeout=10,
timeout=5,
)
return _sector_session
@@ -370,7 +370,7 @@ async def fetch_sector_list(sector_type: str) -> list[dict]:
set_cache(cache_key, json.dumps(push2_data, ensure_ascii=False), ttl_hours=ttl)
return push2_data
# push2 失败,用 akshare
# push2 失败,用 akshare(不写磁盘缓存)
akshare_data = await akshare_task
if akshare_data:
_sector_cache[sector_type] = (akshare_data, now)
@@ -385,14 +385,14 @@ async def _fetch_push2(sector_type: str) -> list[dict]:
session = _get_sector_session()
ut = await get_em_ut()
url = (
f"https://push2.eastmoney.com/api/qt/clist/get"
f"?fs={fs}&fields={SECTOR_FIELDS}"
f"&fid=f62&po=1&pz=500&pn=1&np=1&fltt=2"
f"&invt=2&ut={ut}"
)
for attempt in range(2):
url = (
f"https://push2.eastmoney.com/api/qt/clist/get"
f"?fs={fs}&fields={SECTOR_FIELDS}"
f"&fid=f62&po=1&pz=500&pn=1&np=1&fltt=2"
f"&invt=2&ut={ut}"
)
try:
resp = await session.get(url)
if resp.status_code != 200:
@@ -432,6 +432,11 @@ async def _fetch_push2(sector_type: str) -> list[dict]:
if "disconnect" in err.lower() or "refused" in err.lower() or attempt == 1:
await get_em_ut(force_refresh=True)
ut = _em_ut
# 先尝试更新现有会话的 headers
try:
session.headers.update({"Referer": "https://data.eastmoney.com/bkzj/hy.html"})
except Exception:
pass
# 重建会话(TLS 指纹可能会被缓存)
global _sector_session
_sector_session = None
@@ -445,10 +450,12 @@ import akshare as ak
async def _fetch_akshare(sector_type: str) -> list[dict]:
"""akshare 降级方案(同花顺数据源)"""
"""akshare 降级方案(东方财富数据源)"""
loop = asyncio.get_event_loop()
code_map_key = f"board_codes:{sector_type}"
def _get():
def _build_code_map():
"""获取板块代码映射(HTTP 较慢,结果单独缓存 24h)"""
code_map = {}
try:
if sector_type == "industry":
@@ -469,16 +476,34 @@ async def _fetch_akshare(sector_type: str) -> list[dict]:
code_map[str(r.get("name", ""))] = str(r.get("code", ""))
except Exception:
pass
return code_map
def _get_fund_flow():
if sector_type == "industry":
df = ak.stock_fund_flow_industry()
return ak.stock_fund_flow_industry()
else:
df = ak.stock_fund_flow_concept()
return ak.stock_fund_flow_concept()
return code_map, df
# 1. 尝试从缓存读取 code_map
code_map = {}
cached_map = get_cache(code_map_key)
if cached_map is not None:
code_map = json.loads(cached_map)
try:
code_map, df = await loop.run_in_executor(None, _get)
if code_map:
# 已有缓存,只需获取资金流
df = await loop.run_in_executor(None, _get_fund_flow)
else:
# 首次:code_map + 资金流并发获取
map_data, df = await asyncio.gather(
loop.run_in_executor(None, _build_code_map),
loop.run_in_executor(None, _get_fund_flow),
)
if map_data:
code_map = map_data
set_cache(code_map_key, json.dumps(code_map, ensure_ascii=False), ttl_hours=24)
if df is None or df.empty:
return []
df = df.sort_values("净额", ascending=False)
+2 -2
View File
@@ -458,12 +458,12 @@ export interface SectorResponse {
* 获取东方财富板块列表(按主力净流入排序)
* @param type industry=行业板块, concept=概念板块
*/
export async function fetchSectors(type: SectorType): Promise<SectorItem[]> {
export async function fetchSectors(type: SectorType, signal?: AbortSignal): Promise<SectorItem[]> {
const baseUrl = getApiBaseUrl();
const url = `${baseUrl}/api/sectors?type=${type}`;
try {
const resp = await fetch(url, { method: "GET" });
const resp = await fetch(url, { method: "GET", signal });
if (!resp.ok) return [];
const result: SectorResponse = await resp.json();
return result.data || [];
+12 -4
View File
@@ -1,5 +1,5 @@
import { createFileRoute } from "@tanstack/react-router";
import { useState, useEffect, useMemo } from "react";
import { useState, useEffect, useMemo, useRef } from "react";
import { fetchSectors, type SectorItem, type SectorType } from "@/lib/stock-api";
import { Card, CardContent } from "@/components/ui/card";
import { ArrowLeft, TrendingUp, TrendingDown, RefreshCw } from "lucide-react";
@@ -21,17 +21,25 @@ function SectorsPage() {
const [data, setData] = useState<SectorItem[]>([]);
const [loading, setLoading] = useState(true);
const [sort, setSort] = useState<SortMode>("mainNetInflow");
const abortRef = useRef<AbortController | null>(null);
const loadData = (t: SectorType) => {
// 取消上次未完成的请求
abortRef.current?.abort();
const controller = new AbortController();
abortRef.current = controller;
setLoading(true);
fetchSectors(t).then((items) => {
setData(items);
setLoading(false);
fetchSectors(t, controller.signal).then((items) => {
if (!controller.signal.aborted) {
setData(items);
setLoading(false);
}
});
};
useEffect(() => {
loadData(tab);
return () => abortRef.current?.abort();
}, [tab]);
const sorted = useMemo(() => {