#!/usr/bin/env python3 """ 用 Browserless 抓 Yahoo 三大法人頁,輸出精簡摘要(代號可變) 用法: python3 stocks/yahoo-institutional-browserless.py 3481 python3 stocks/yahoo-institutional-browserless.py 2330 """ import json import os from pathlib import Path import re import sys import subprocess def load_dotenv() -> None: """載入 .env(優先目前目錄,其次 workspace 根目錄)。""" candidates = [ Path.cwd() / ".env", Path(__file__).resolve().parents[3] / ".env", ] for env_path in candidates: if not env_path.exists(): continue for raw in env_path.read_text(encoding="utf-8").splitlines(): line = raw.strip() if not line or line.startswith("#") or "=" not in line: continue k, v = line.split("=", 1) k = k.strip() v = v.strip().strip('"').strip("'") if k and k not in os.environ: os.environ[k] = v break load_dotenv() ENDPOINT = os.getenv("BROWSERLESS_ENDPOINT") TOKEN = os.getenv("BROWSERLESS_TOKEN") if not ENDPOINT or not TOKEN: raise RuntimeError("缺少必要環境變數:BROWSERLESS_ENDPOINT / BROWSERLESS_TOKEN") STOCK_NO = sys.argv[1] if len(sys.argv) > 1 else "3481" def fetch_html(stock_no: str) -> str: url = f"https://tw.stock.yahoo.com/quote/{stock_no}.TW/institutional-trading" api = f"{ENDPOINT}/content?token={TOKEN}" payload = json.dumps( { "url": url, "gotoOptions": {"waitUntil": "domcontentloaded", "timeout": 60000}, }, ensure_ascii=False, ) cmd = [ "curl", "-sS", "--max-time", "90", "-X", "POST", api, "-H", "Content-Type: application/json", "-d", payload, ] try: text = subprocess.check_output(cmd, text=True, timeout=95) except subprocess.CalledProcessError as e: raise RuntimeError(f"Browserless 呼叫失敗: {e}") from e except subprocess.TimeoutExpired as e: raise RuntimeError("Browserless 呼叫逾時") from e if "TimeoutError" in text: raise RuntimeError("Browserless 導航逾時(TimeoutError)") return text def pick(html: str, pattern: str, default: str = "-") -> str: m = re.search(pattern, html, flags=re.S) return m.group(1) if m else default def parse_inst(html: str, stock_no: str) -> dict: name = pick(html, r'property="og:title" content="([^"(]+)\([0-9]+\.TW\)', "").strip() if not name: name = pick(html, r"