#!/usr/bin/env python3 # -*- coding: utf-8 -*- """hk_fundamentals_collector.py — 港股通基本面采集(PE/PB/市值,阶段2数据补采) 数据源:腾讯批量行情 hk 前缀(qt.gtimg.cn/q=hk00700,...)。 复用 fundamentals_full_refresh 的解析模式,港股字段位置: pe=parts[39] pb=parts[43](A股是46,港股是43) 总市值=parts[44] 流通市值=parts[45](亿) 写入:stock_fundamentals 表(与A股同表复用,code=5位港股代码,eps=price/pe)。 调度:每日盘后(随A股基本面刷新节奏)。 限速:批量100只/批,批间0.5s。 """ import sqlite3 import sys import time import urllib.request # ── 消息通道统一路由(broadcast/xmpp by delivery) ── try: from messenger import install_stdio_hook as _msh _msh() except Exception: pass DB_PATH = "/home/hmo/MoFin/data/mofin.db" UA = "Mozilla/5.0" BATCH = 100 def fetch_hk_batch(codes): """腾讯批量行情(hk前缀,每批100只),返回 {code: {pe,pb,price,mcap_total,mcap_flow}}""" if not codes: return {} results = {} symbols = [f"hk{c}" for c in codes] url = f"http://qt.gtimg.cn/q={','.join(symbols)}" try: req = urllib.request.Request(url, headers={"User-Agent": UA}) opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) with opener.open(req, timeout=15) as r: text = r.read().decode("gbk", errors="ignore") for line in text.strip().split("\n"): if "~" not in line: continue parts = line.split("~") if len(parts) < 46: continue code = parts[2] # 港股代码(00700) price = float(parts[3]) if parts[3] else 0 pe = float(parts[39]) if parts[39] else 0 pb = float(parts[43]) if parts[43] else 0 # 港股 PB 在 43(A股在46) mcap_t = float(parts[44]) if parts[44] else 0 # 总市值(亿HKD) mcap_f = float(parts[45]) if parts[45] else 0 # 流通市值(亿HKD) if price > 0 and code: results[code] = {"price": price, "pe": pe, "pb": pb, "mcap_total": mcap_t, "mcap_flow": mcap_f} except Exception as e: print(f" batch fetch error: {str(e)[:60]}", flush=True) return results def main(): conn = sqlite3.connect(DB_PATH, timeout=30) conn.execute("PRAGMA busy_timeout=30000") codes = [r[0] for r in conn.execute( "SELECT code FROM hk_connect_stocks WHERE is_active=1 ORDER BY code").fetchall()] print(f"港股通基本面采集:{len(codes)} 只(腾讯行情 PE/PB/市值)", flush=True) ok = 0 t0 = time.time() for i in range(0, len(codes), BATCH): batch = codes[i:i + BATCH] data = fetch_hk_batch(batch) for code, d in data.items(): eps = round(d["price"] / d["pe"], 2) if d["pe"] > 0 else 0 conn.execute( "INSERT OR REPLACE INTO stock_fundamentals (code, pe, pb, eps, mcap_total, mcap_flow, updated_at) " "VALUES (?,?,?,?,?,?,datetime('now','localtime'))", (code, d["pe"], d["pb"], eps, d["mcap_total"], d["mcap_flow"])) ok += 1 conn.commit() print(f" [{i+len(batch)}/{len(codes)}] 本批{len(data)}只 | {time.time()-t0:.0f}s", flush=True) time.sleep(0.5) n = conn.execute("SELECT COUNT(*) FROM stock_fundamentals WHERE length(code)=5").fetchone()[0] sample = conn.execute( "SELECT code, pe, pb, mcap_total FROM stock_fundamentals WHERE code IN ('00700','09988','01810')").fetchall() conn.close() print(f"\n完成:{ok} 只入 stock_fundamentals(港股 {n} 条),耗时{time.time()-t0:.0f}s", flush=True) print("样例(code/pe/pb/总市值亿HKD):", flush=True) for r in sample: print(f" {r[0]}: pe={r[1]} pb={r[2]} mcap={r[3]:.0f}亿", flush=True) return 0 if __name__ == "__main__": sys.exit(main())