New crawler: app/crawlers/openinsider_top.py\n- Scrapes three pages (sales/purchases/officer purchases)\n- Filters rows with Value/Amount >= ,000,000 (configurable via INSIDER_MIN_AMOUNT)\n- Builds concise notifications; saves to data/openinsider_top.json Runner: support comma-separated CRAWLER_TYPE and new openinsider_top type\n- Accepts e.g., CRAWLER_TYPE=openinsider_top,openinsider,barrons\n- Preserves order, removes duplicates; warns on unknown types\n- Uses shared schedule: RUN_DAILY_AT or CHECK_INTERVAL; initial run per crawler Entrypoint: rename enhanced_crawler.py -> main.py\n- Update Dockerfile CMD and README references Config & docs:\n- Reorganize .env.template into clear sections with examples\n- Update .env with multi-crawler example and INSIDER_MIN_AMOUNT\n- README: document new crawler, usage, and multi-type CRAWLER_TYPE
152 lines
6.1 KiB
Markdown
152 lines
6.1 KiB
Markdown
# 股票爬蟲服務(模組化架構)
|
||
|
||
可擴充的股票爬蟲服務,內建 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_<SYMBOL>.json`
|
||
|
||
## 擴充新站點(建議流程)
|
||
1) 複製範本:`app/crawlers/template.py` → `app/crawlers/<your_site>.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)爬蟲
|