From 23686e28a1613a3c8fc2cf4895a5bf1c59379f9a Mon Sep 17 00:00:00 2001 From: hmo Date: Fri, 14 Aug 2026 01:57:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E9=97=BB=E9=87=87=E9=9B=86?= =?UTF-8?q?=E5=88=86=E5=B1=82=E2=80=94=E2=80=94=E7=9B=98=E5=89=8D=E5=85=A8?= =?UTF-8?q?=E9=87=8F(BATCHES=204=E2=86=928,=E6=AF=8F=E6=89=B9534=E5=8F=AA?= =?UTF-8?q?=C3=970.9s=3D481s=E5=AE=89=E5=85=A8)=20+=20=E7=9B=98=E4=B8=AD?= =?UTF-8?q?=E6=B1=A0=E5=AD=90=E5=A2=9E=E9=87=8F(pool=5Fnews=5Fcollector,?= =?UTF-8?q?=E8=87=AA=E9=80=89+=E6=8C=81=E4=BB=9382=E5=8F=AA=C3=970.9s=3D74?= =?UTF-8?q?s,*/30=E7=9B=98=E4=B8=AD=E9=AB=98=E9=A2=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/profile-scripts/news_collector_full.py | 2 +- deploy/profile-scripts/pool_news_collector.py | 103 ++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 deploy/profile-scripts/pool_news_collector.py diff --git a/deploy/profile-scripts/news_collector_full.py b/deploy/profile-scripts/news_collector_full.py index 3c9a3b88..47d63203 100644 --- a/deploy/profile-scripts/news_collector_full.py +++ b/deploy/profile-scripts/news_collector_full.py @@ -22,7 +22,7 @@ from news_collector import fetch_stock_news, init_table DB_PATH = Path("/home/hmo/MoFin/data/mofin.db") SLEEP = 0.4 # 请求间隔(防东财封) -BATCHES = 4 # 4 批 +BATCHES = 8 # 8 批(2026-08-13 由4改8:4272只/4批=1068只×0.9s=961s超600s超时,改8批每批534只×0.9s=481s安全) RECENT_DAYS = 3 # 只存近 3 天新闻(news3 因子口径) diff --git a/deploy/profile-scripts/pool_news_collector.py b/deploy/profile-scripts/pool_news_collector.py new file mode 100644 index 00000000..8c84ae38 --- /dev/null +++ b/deploy/profile-scripts/pool_news_collector.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""pool_news_collector.py — 池子新闻采集(自选+持仓,盘中高频) + +2026-08-13 新增(老莫架构):盘前全量跑(选股),盘中只跑池子里的(自选+持仓),增加频率。 +- 盘前:news_collector_full b0-b7(全市场 4272 只,8批) +- 盘中:pool_news_collector(池子 82 只,*/30 盘中) + +池子 = 持仓(holdings shares>0) + 自选(holding_strategies 自选策略) +""" +import sys, os, json, time, sqlite3 +from datetime import datetime, timedelta +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent)) +from news_collector import fetch_stock_news, init_table + +DB_PATH = Path("/home/hmo/MoFin/data/mofin.db") +SLEEP = 0.4 # 请求间隔(防东财封) +RECENT_DAYS = 3 # 只存近 3 天新闻(news3 因子口径) + + +def _singleton_guard(tag): + lock_dir = Path("/tmp/mofin_locks") + lock_dir.mkdir(exist_ok=True) + try: + import fcntl + fd = os.open(str(lock_dir / f"{tag}.lock"), os.O_CREAT | os.O_RDWR) + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + return fd + except OSError: + return None + + +def get_pool_codes(conn): + """池子:持仓 + 自选(去重)""" + codes = set() + # 持仓 + for r in conn.execute("SELECT DISTINCT code FROM holdings WHERE is_active=1 AND shares>0"): + codes.add(r[0]) + # 自选 + for r in conn.execute("SELECT DISTINCT code FROM holding_strategies WHERE status='active' AND decision_type='自选策略'"): + codes.add(r[0]) + return sorted(codes) + + +def main(): + tag = "pool_news_collector" + guard = _singleton_guard(tag) + if guard is None: + print(f"[{tag}] 已有实例在运行,跳过", file=sys.stderr) + return + + conn = sqlite3.connect(DB_PATH) + init_table(conn) + cur = conn.cursor() + + codes = get_pool_codes(conn) + print(f"[{tag}] {datetime.now().strftime('%H:%M:%S')} 池子新闻采集 开始", flush=True) + print(f" 池子: {len(codes)} 只(持仓+自选)", flush=True) + + since = (datetime.now() - timedelta(days=RECENT_DAYS)).strftime("%Y-%m-%d") + t0 = time.time() + ok = empty = fail = new = 0 + + for idx, code in enumerate(codes, 1): + try: + arts = fetch_stock_news(code, max_pages=1) + if not arts: + empty += 1 + continue + n = 0 + for a in arts: + dstr = (a.get("date") or "")[:10] + if dstr and dstr >= since: + try: + cur.execute( + "INSERT OR IGNORE INTO stock_news (code, title, content, source, date, url) " + "VALUES (?,?,?,?,?,?)", + (code, a.get("title", ""), a.get("content", ""), + a.get("source", "eastmoney"), a.get("date", ""), a.get("url", ""))) + if cur.rowcount: + n += 1 + except Exception: + pass + new += n + ok += 1 + except Exception as e: + fail += 1 + if fail <= 5: + print(f" FAIL {code}: {str(e)[:60]}", flush=True) + if idx % 20 == 0: + conn.commit() + print(f" [{idx}/{len(codes)}] ok={ok} empty={empty} fail={fail} 新增={new} | {time.time()-t0:.0f}s", flush=True) + time.sleep(SLEEP) + + conn.commit() + conn.close() + dt = time.time() - t0 + print(f"[{tag}] 完成: {ok}/{len(codes)} 成功, {empty} 无新闻, {fail} 失败, 新增 {new} 条, 耗时 {dt:.0f}s", flush=True) + + +if __name__ == "__main__": + main()