# 股票爬蟲服務(模組化架構) 可擴充的股票爬蟲服務,內建 HTTP API 與多種通知(Email/Webhook/Discord)。 目前提供三類爬蟲: - Barron's 股票推薦 - OpenInsider 內部人交易(支援多標的) - OpenInsider 當日大額內部人交易(跨三頁合併、金額過濾) ## 功能 - 定時抓取(支援每 N 秒或每日固定時間) - 只在有新內容時發送通知(可設定首次啟動也通知) - 內建 `/health`、`/info`、`/stats`、`/check` 與 `/notify_test` API - Docker 化部署,資料與日誌可持久化 - 架構模組化,日後可擴充其他站點 ## 專案結構 ``` app/ runner.py # 啟動流程(載入設定、啟動 API 與爬蟲) config.py # 設定載入與 logging api/server.py # Flask API crawlers/base.py # BaseCrawler:通用排程/比對/通知 crawlers/barrons.py # Barron’s 爬蟲 crawlers/openinsider.py # OpenInsider 內部人交易爬蟲(多標的) crawlers/template.py # 新站點範本(複製後改名擴充) services/storage.py # JSON 儲存 services/notifications.py # Email/Webhook/Discord main.py # 入口點,委派到 app.runner Dockerfile docker-compose.yml requirements.txt health_check.py .env / .env.template data/ # 持久化資料(預設) logs/ # 持久化日誌(預設) ``` ## 快速開始(Docker) 1) 建立環境變數檔 ```bash cp .env.template .env # 編輯 .env,至少設定你要用到的通知方式(Email/Webhook/Discord 任選) ``` 2) 啟動服務 ```bash docker-compose up -d docker-compose logs -f barrons-crawler ``` 3) 驗證 API ```bash curl http://localhost:8080/health curl http://localhost:8080/stats | jq curl http://localhost:8080/check | jq curl "http://localhost:8080/notify_test?channel=email" # 或 webhook/discord ``` ## 本機執行(非 Docker) ```bash pip install -r requirements.txt python main.py ``` ## 環境變數說明 - 基本 - `CHECK_INTERVAL`: 檢查間隔(秒),預設 300(若設定了 `RUN_DAILY_AT` 則忽略) - `RUN_DAILY_AT`: 每天固定時間(例如 `12:00`),使用容器本機時區 - `LOG_LEVEL`: 日誌等級,預設 `INFO`(可 `DEBUG`) - `ALWAYS_NOTIFY_ON_STARTUP`: 是否在啟動後第一次就寄出目前清單(true/false),預設 false - Email(可選) - `EMAIL_SMTP_SERVER`、`EMAIL_SMTP_PORT`(587/465/25) - `EMAIL_SMTP_SECURITY`: starttls | ssl | none(預設 starttls) - `EMAIL_FROM`、`EMAIL_TO`、`EMAIL_USERNAME`、`EMAIL_PASSWORD` - Webhook(可選) - `WEBHOOK_URL`: Slack/Teams Incoming Webhook URL - Discord(可選) - `DISCORD_WEBHOOK`: Discord Webhook URL - 進階路徑(可選) - `DATA_DIR`: 資料輸出路徑(Docker 預設 `/app/data`;本機預設 `./data`) - `LOG_DIR`: 日誌輸出路徑(Docker 預設 `/app/logs`;本機預設 `./logs`) - 爬蟲選擇與參數 - `CRAWLER_TYPE`: `barrons` | `openinsider` | `openinsider_top`(可用逗號同時啟多種,例如:`openinsider_top,openinsider`) - Barron's:無額外參數 - OpenInsider(依個別股票查詢): - 單一標的:`SYMBOL=PLTR` - 多個標的:`SYMBOLS=PLTR,NVDA,TSLA` - OpenInsider 當日大額(跨頁合併): - 來源頁面: - `http://openinsider.com/top-insider-sales-of-the-day` - `http://openinsider.com/top-insider-purchases-of-the-day` - `http://openinsider.com/top-officer-purchases-of-the-day` - 金額門檻(含千分位、自動去 `$`):`INSIDER_MIN_AMOUNT`,預設 `1000000` Email 使用建議: - Gmail 請使用「應用程式密碼」並開啟兩步驟驗證 - 校園/企業信箱請向管理者確認 SMTP 主機、連接埠與加密方式 ## Web API 端點 - `GET /health`: 健康檢查 - `GET /info`: 當前爬蟲資訊(多實例時回傳陣列) - `GET /stats`: 目前統計資訊(單實例為物件,多實例為 map) - `GET /check`: 立即執行一次檢查(多實例會對每個爬蟲都執行) - `GET /notify_test?channel=email|webhook|discord`: 測試通知 ## 健康檢查與維運 - 容器內建 HEALTHCHECK(每 30 秒檢查一次 `/health`,連續 3 次失敗標記為不健康) - 常用指令 ```bash docker-compose build docker-compose up -d docker-compose restart docker-compose logs -f barrons-crawler docker-compose down ``` ## 資料與日誌 - 預設位置(Docker):`/app/data`、`/app/logs`(已透過 volume 映射至宿主 `./data`、`./logs`) - 檔案格式(以 Barron’s 為例:`data/barrons_data.json`) - `last_update`: ISO 時間 - `stock_picks`: 文章清單(title/link/hash/scraped_at) - `stats`: 執行統計 - OpenInsider 多標的:`data/openinsider_.json` ## 擴充新站點(建議流程) 1) 複製範本:`app/crawlers/template.py` → `app/crawlers/.py` 2) 實作兩個方法: - `fetch_page(self) -> Optional[str]`:抓取 HTML - `parse_items(self, html: str) -> List[Dict]`:輸出包含 `title`(必要)、建議 `link`、`hash`、`scraped_at` 3) 啟動方式: - 簡單做法:為新站點建立第二個容器(複製服務段落,指定不同資料檔/埠口) - 進階做法:在 `app/runner.py` 啟動多個爬蟲與多站 API(需擴充 API 路由與執行緒管理) ## 故障排除 - 取不到網頁:檢查網路、User-Agent、目標網站是否改版 - Email 失敗:確認 SMTP 設定、應用程式密碼、連接埠與加密方式 - Barron's 解析不到內容:查看日誌,更新選擇器邏輯 - OpenInsider 解析不到內容:檢查 `SYMBOL/SYMBOLS` 是否正確,觀察是否被站方限流 - 服務無回應:檢查容器日誌與健康檢查狀態 ## 安全建議 - 不要把密碼放到版本控制;使用 `.env` 並將其列入 `.gitignore` - 適度限制通知頻率與內容,避免濫用 - 若對外開放 API,建議加上認證與 HTTPS ## 版本記事 - 2025-09: - 重構為模組化架構,API 與爬蟲邏輯分離 - 新增 OpenInsider 內部人交易爬蟲與多標的支援 - 新增 OpenInsider 當日大額內部人交易(≥$1,000,000)爬蟲