高頻率監控政府公告(目前內建 DGPA 停班停課資訊)的開源專案。系統會定期抓取官網 HTML、解析至縣市粒度、比對資料庫紀錄並透過 SendGrid 或模擬發信輸出通知。整個服務以 Docker 常駐,符合本地開發與雲端長時間運行的需求。
- 縣市級解析:
DGPAProvider會將table#Table解析成CityStatus結構,逐縣市記錄多行敘述並計算獨立雜湊值。 - 差異偵測:
provider_status紀錄整體 hash,provider_city_status追蹤每個縣市 hash;只要任一層有變化就觸發通知並更新資料庫。 - 通知模組可回退:未設定 SendGrid API Key 或寄件資訊時,自動落入模擬發信,仍會輸出完整 HTML 與變動縣市清單於 log,方便在無憑證環境除錯。
- 可延伸 Provider Pattern:所有資料來源皆繼承
BaseProvider,支援跨網站擴充與獨立測試。 - 容器化部署:提供
Dockerfile,只需掛載 SQLite 資料夾即可長時間運行。
┌────────────┐ ┌──────────┐ ┌────────────┐
│ Providers │ ───▶ │ main.py │ ───▶ │ notifier.py │
└────────────┘ │ + loop │ └────────────┘
│ │ + diff │ │
▼ └────┬─────┘ ▼
┌────────────┐ │ ┌──────────────┐
│ SQLite DB │ ◀─────────┘ │ SendGrid/Log │
└────────────┘ └──────────────┘
src/providers/base_provider.py:定義ScrapeResult與CityStatus。src/providers/dgpa_provider.py:負責抓取 DGPA 頁面、解析更新時間與縣市表格。src/main.py:初始化 log、載入 Provider、執行無限迴圈、決定是否發通知並更新 DB。src/database.py:包裝 SQLite 連線與provider_status/provider_city_status兩張表的 CRUD。src/notifier.py:產生 HTML 郵件內容並呼叫 SendGrid;未設定憑證時輸出模擬訊息。
更多架構細節可參考 docs/architecture.md 與 docs/dgpa_city_monitoring.md。
- Python 3.12+(本 repo 已透過
.venv驗證) - Docker 28+(用於部署與驗證)
- 可選:SendGrid API Key(實際寄送 Email)
cd monitoring_service
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
# 第一次執行會初始化 SQLite (預設 /app/data/status.db)
python -m src.main- 若要結束服務請按
Ctrl+C。 - 在開發過程中可改
config.ACTIVE_PROVIDERS以新增或移除 Provider。
| 變數 | 預設值 / 需求 | 說明 |
|---|---|---|
MONITORING_INTERVAL_SECONDS |
60 |
每輪迴圈的睡眠秒數。 |
SENDGRID_API_KEY |
必填(可留空以使用模擬) | SendGrid API Key。 |
RECIPIENT_EMAIL |
必填(可留空以使用模擬) | 通知收件者。 |
SENDER_EMAIL |
必填(可留空以使用模擬) | 通知寄件者。 |
DATABASE_PATH |
/app/data/status.db |
SQLite 檔案路徑,可透過 Volume 指定。 |
LOG_LEVEL |
INFO |
Logging 等級。 |
未設定 SendGrid 相關變數時,系統會自動改用模擬發信模式並在 log 中輸出
[Simulated Notification],其餘邏輯(DB 更新、hash 比對)仍照常執行。
cd monitoring_service
docker build -t monitoring_service .
# 持續運行並掛載資料庫資料夾
docker run -d --name monitoring_service \
-e MONITORING_INTERVAL_SECONDS=60 \
-e SENDGRID_API_KEY=SG.xxxxxx \
-e RECIPIENT_EMAIL=you@example.com \
-e SENDER_EMAIL=bot@example.com \
-v $(pwd)/data:/app/data \
monitoring_service
# 觀察 log
docker logs -f monitoring_service- 單元測試:
pytest -q。新增的測試檔涵蓋 Provider 解析、資料庫 CRUD、通知模擬與 city block HTML。 - 假資料:
analysis/dgpa.html作為 fixture,能離線測試 DOM 解析。 - Docker 驗證:
timeout 60 docker run ...可快速確認整個流程與模擬發信輸出。
main.py啟動時會呼叫database.init_db(),確保兩張表存在。- 每個 Provider 實例的
run()回傳ScrapeResult,其中content用於整體 hash,city_statuses用於縣市 hash。 run_monitoring_cycle會:- 比對整體 hash;若不同則記錄為變更。
- 逐一比對
city_statuses,只要任一縣市內容有變化,會被加入changed_cities。 - 只要上述任一條件成立就觸發
notifier.send_notification(),並同時更新provider_status與provider_city_status。
- 通知內的「縣市變化摘要」會列出所有異動縣市與最新敘述。
- 在
src/providers/新增檔案與類別(例如taipei_gov_provider.py)。 - 繼承
BaseProvider並實作run(),通常拆分fetch_content()與parse()方便測試。 - 回傳的
ScrapeResult可視需求填入city_statuses。 - 將類別名稱加入
config.ACTIVE_PROVIDERS。 - 撰寫對應測試並更新
requirements.txt(若使用額外套件)。
- SQLite 檔案位於
DATABASE_PATH,建議於 Docker 執行時掛載 Host 目錄(例如./data)。 provider_status儲存每個 Provider 的最後 hash;provider_city_status儲存同一 Provider 所有縣市的 hash 與時間戳。- 若想手動檢視資料,可使用
sqlite3 status.db '.tables'或簡單腳本輸出。
- SendGrid 沒設定會怎樣? → 系統會記錄 WARNING 並輸出模擬 HTML,不會中斷。
- 為什麼 docker run 命令會結束? → 如果使用
timeout進行短期驗證,時間到就會強制停止。長期部署請移除timeout並用docker logs -f觀察。 - 如何調整輪詢頻率? → 更改
MONITORING_INTERVAL_SECONDS,最小值建議 >5 秒,以免對資料源造成過大壓力。 - 網站 robots.txt 限制? → 請確認有權限向資料來源自動抓取;若需手動快照,可放在
analysis/供測試使用。
歡迎提交 Issue 或 PR,一起擴充更多政府/氣象來源的即時監控能力!