changedetection.io 是一套開源、可自架的網頁變更監控工具(GitHub 34,000+ stars、Apache-2.0、Python 撰寫):你只要貼上一個網址,它就會定期去比對頁面內容,只要發現差異,就透過 Apprise 推播到 ntfy、Telegram、Discord、Email 或 Webhook。 它最常被拿來做三件事:盯商品降價、盯補貨、盯各種「只會默默更新在官網上」的公告。
這篇文章會從零帶你自架一套,並且把「怎麼只在你真正在意的那一行文字改變時才通知你」這個關鍵技巧講清楚——因為「什麼都通知」等於什麼都沒通知。
為什麼需要它?Uptime Kuma 管「活著沒」,它管「變了沒」
如果你已經在用 Uptime Kuma 之類的工具,先分清楚兩件事:
| 工具類型 | 它回答的問題 | 例子 |
|---|---|---|
| 可用性監控(Uptime Kuma、Gatus) | 服務還活著嗎? | 網站回 503 了,趕快叫我 |
| 內容變更監控(changedetection.io) | 內容變了嗎? | 官網還活著,但價格從 8,990 變成 6,990 |
網頁最麻煩的變更,往往是「頁面完全正常、HTTP 200、只是某一小段文字不一樣」。這種事沒有工具幫你盯,就只能自己每天手動重刷。changedetection.io 就是來解決這件事的。
實際會用到它的情境:
- 電商商品降價或補貨(白色 48 號有貨了叫我)
- 政府機關/公會/學校公告異動(招標、法規、榜單、課程名額)
- 公司職缺頁釋出新職位、BambooHR 之類的平台更新
- B2B 競品定價頁改版、條款更新
- PDF 文件改版(它有內建 PDF 文字變更偵測)
- JSON API 回應內容變動(不是看 HTTP 狀態,是看 body 內容)
專案基本資料
| 項目 | 內容 |
|---|---|
| GitHub | dgtlmoon/changedetection.io |
| Stars | 約 34,700(2026-10 統計) |
| 授權 | Apache-2.0(商用自架沒問題) |
| 語言 | Python |
| 最新版本 | 0.60.8(2026-09-28 發布) |
| Docker 映像 | dgtlmoon/changedetection.io 或 ghcr.io/dgtlmoon/changedetection.io |
| 官方 SaaS | 約 US$8.99/月(不想自架時的選項) |
| 資源需求 | 很輕,1 vCPU/512MB RAM 就能跑(要開 Chrome 才需要更多) |
5 分鐘安裝:Docker Compose
最省事的方式是 Docker Compose。先建一個資料夾,放一份 docker-compose.yml:
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io
container_name: changedetection
hostname: changedetection
volumes:
- changedetection-data:/datastore
ports:
- 127.0.0.1:5000:5000
restart: unless-stopped
environment:
- TZ=Asia/Taipei
- BASE_URL=https://change.example.com
volumes:
changedetection-data:
啟動:
docker compose up -d
docker compose logs -f --tail=50
打開 http://127.0.0.1:5000 就會看到介面。注意這裡刻意綁在 127.0.0.1,原因是這個服務內含你所有監控清單,直接開到公網等於把情報公開。要從外部連進來,正確做法是走反向代理(Cloudflare Tunnel 完整教學 或 Caddy/Nginx),而不是改 0.0.0.0。
如果你不想寫 compose,一行也行:
docker run -d --restart always \
-p "127.0.0.1:5000:5000" \
-v datastore-volume:/datastore \
--name changedetection.io \
dgtlmoon/changedetection.io
讓它能抓 JavaScript 網站(強烈建議)
很多現代電商頁面的價格是 JS 執行後才塞進 DOM,純 HTTP fetcher 抓到的會是空殼。解法是加跑一個 Chrome:
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io
volumes:
- changedetection-data:/datastore
ports:
- 127.0.0.1:5000:5000
restart: unless-stopped
environment:
- PLAYWRIGHT_DRIVER_URL=ws://browser-sockpuppet-chrome:3000
depends_on:
browser-sockpuppet-chrome:
condition: service_started
browser-sockpuppet-chrome:
hostname: browser-sockpuppet-chrome
image: dgtlmoon/sockpuppetbrowser:latest
cap_add:
- SYS_ADMIN
restart: unless-stopped
environment:
- SCREEN_WIDTH=1920
- SCREEN_HEIGHT=1024
- SCREEN_DEPTH=16
- MAX_CONCURRENT_CHROME_PROCESSES=10
volumes:
changedetection-data:
設定好 PLAYWRIGHT_DRIVER_URL 後,每個 watch 就能在編輯頁切換 fetcher 成 Chrome,並解鎖 Visual Selector(用滑鼠點你要監控的那一塊)與 Browser Steps。記憶體會多吃一些,1GB RAM 以上比較保險。
建立第一個 watch
介面右上角「+ ADD WEBSITE TO WATCH」貼上網址即可。重點是這三個欄位:
- URL:要監控的頁面。
- Check interval:多久檢查一次(預設都有值,建議至少 15 分鐘起跳)。
- Filters & Triggers(下面會細講):決定「哪些內容算變更」。
存檔後按「Recheck」可以立刻驗證抓不抓得到。抓到了,它會存下一份快照,之後有差異才會通知你。
關鍵技術一:把「雜訊」濾掉的選擇器
沒有設選擇器,一個頁面上任何一點改動(廣告輪播、時間戳、推薦商品)都會通知你。 這是新手最常見的失敗原因。changedetection.io 支援:
| 語法 | 寫法 | 適合 |
|---|---|---|
| CSS Selector | #price、.product-price | 一般 HTML 頁面,最簡單 |
| XPath | //span[@class="price"] | 結構複雜、要往上找相對節點 |
| JSONPath | json:$..price | JSON API 回應 |
| jq | jq:.items[] | select(.inStock) | 複雜 JSON 過濾與邏輯判斷 |
| 正規表達式 | re:test:、re:match:、re:replace: | 文字層級的抽取與清理 |
還有一組「清理用」的開關,跟選擇器搭著用最有效:
- Subtractive selectors:先把頁首、頁尾、導覽列整塊刪掉再比對。
- Ignore text:符合的字串(例如
版權所有 2026、隨機時間戳)直接忽略。 - Trigger text:只有當頁面出現這些字時才算變更。
- Text should not be present:出現就告警(適合監控「已售完」字樣消失)。
- Extract text:用 regex 只留下你要的那一小段(例如只留價格數字)。
電商價格:優先抓 JSON-LD,比爬 DOM 穩
很多電商(含台灣的購物網站)會在頁面裡埋 application/ld+json 的結構化資料,裡面就有 price 與 availability。這比抓看得見的 <div> 可靠太多,因為前端改版常常只改 CSS class、不改結構化資料。用 JSON filter 直接寫:
json:$..price
就能把整份 JSON-LD 裡的 price 抽出來比對。若你要更複雜的條件(例如只在 availability 是 InStock 時才比價格),改用 jq: 版本。
關鍵技術二:只在「真的降價」時通知你
如果你的需求是價格,不要自己寫選擇器,直接用內建的 Re-stock & Price detection 處理器(restock_diff)。在編輯頁把處理器切成價格/補貨模式後,它會自動讀取頁面的價格 meta 資料,並給你這些選項:
- Follow price changes:跳脫「有變就通知」,改追蹤價格趨勢。
- Price change threshold:只有變動超過 X% 才通知(避免 1 元跳動轟炸你)。
- In stock only:只在「補貨」那一刻通知。
- 還能把歷史價格畫成趨勢圖,看它是不是真的在慢慢降。
搭配 Conditions(ALL / ANY 邏輯)可以做出很精準的規則,例如:
{
"conditions": [
{ "field": "page_filtered_text", "operator": "contains_regex", "value": "6,?990" }
],
"conditions_match_logic": "ANY"
}
關鍵技術三:登入後才看得到的頁面(Browser Steps)
有些內容要登入、要選規格、要按下「查詢」才會出現。Browser Steps 讓你在每次檢查前先跑一串瀏覽器動作:
- 開啟頁面
- 填入文字框(帳號/關鍵字/日期)
- 點擊按鈕(同意 cookie、加入購物車、送出查詢)
- 才執行變更比對
它需要 Playwright 已啟用(也就是上面那段 sockpuppetbrowser 的設定)。設定完 Browser Steps 之後,再回 Visual Selector 分頁用滑鼠點出真正要盯的元素,整個流程就穩了。
通知:透過 Apprise 串接 100+ 種服務
changedetection.io 的通知底層用的是 Apprise,所以你能填的是「通知 URL」。在每個 watch 的 Edit → Notifications 貼上,或直接在 Settings 設成全站預設:
| 服務 | 通知 URL 格式 |
|---|---|
| ntfy(自架或 ntfy.sh) | ntfy://ntfy.sh/my-changes |
| ntfy(自架含帳密) | ntfy://user:pass@ntfy.example.com/my-topic |
| Telegram | tgram://<bot_token>/<chat_id> |
| Discord | discord://<WebhookID>/<WebhookToken>/ |
| Gotify | gotify://hostname/token |
| Email(Gmail 應用程式密碼) | mailto://user:app_password@gmail.com |
| 一般 Webhook/自家 API | json://your-server.com/hook |
幾點實務建議:
- 優先用 ntfy:自架一個 ntfy 容器,手機裝 App 訂閱 topic,完全免費又即時。搭配 Apprise 幾乎零設定。
- 進階:只在特定條件才通知,用
trigger_text(出現「降價」才通知)。 - 加上截圖:開啟
notification_screenshot,通知會附上變更當時的畫面,回頭查很方便。 - 通知也可以模板化:標題與內容支援 Jinja2,能塞入
{{diff}}、{{watch_url}}等變數。
用 AI 過濾雜訊(可接本機 Ollama)
0.6x 之後加入的 LLM 功能,解決的是「diff 太多行、看不出重點」的問題:
- AI 變更意圖(llm_intent):用一句白話寫「只有價格低於 50 美元才通知我」「忽略導覽列與頁尾的變動」,AI 會拿每次 diff 去對照,不符的就自動靜音。
- AI 變更摘要(llm_change_summary):通知不再是落落長的 diff,而是「價格從 NT$8,990 降到 NT$6,990」這種人話。
它支援 OpenAI、Gemini、Anthropic,也能透過 LiteLLM 指向任何 OpenAI 相容端點——包含你本機的 Ollama 或 vLLM,這樣完全不用把監控頁面內容送去第三方。要用本機模型有兩個小訣竅:把 Ollama 的 num_ctx 開到 8192 以上,並視情況調高 LLM_RESTOCK_MAX_CONTENT_CHARS(預設 15000),否則價格藏在頁面深處時會被截掉。
要完全關掉 AI 功能,設環境變數 LLM_FEATURES_DISABLED=true 即可。
用 REST API 自動化:批量建立 watch
手動加監控很累,尤其是你有 50 個商品要盯。changedetection.io 有完整的 REST API(API key 在 Settings → API):
curl -X GET "http://127.0.0.1:5000/api/v1/watch" \
-H "x-api-key: YOUR_API_KEY"
用 Python 批量新增(可直接執行):
import requests
API = "http://127.0.0.1:5000/api/v1"
KEY = "YOUR_API_KEY"
headers = {"x-api-key": KEY, "Content-Type": "application/json"}
targets = [
("https://shop.example.com/item/123", "商品 A 價格"),
("https://shop.example.com/item/456", "商品 B 價格"),
]
for url, title in targets:
payload = {
"url": url,
"title": title,
"time_between_check": {"minutes": 30},
"processor": "restock_diff", # 價格/補貨模式
"follow_price_changes": True,
"price_change_threshold_percent": 3, # 變動 3% 以上才通知
"notification_urls": ["ntfy://ntfy.sh/my-changes"],
"notification_screenshot": True,
"track_ldjson_price_data": True,
}
r = requests.post(f"{API}/watch", headers=headers, json=payload)
print(r.status_code, title, r.text[:80])
其他常用端點:GET /watch/{uuid}(查單一)、PUT /watch/{uuid}(改設定)、DELETE /watch/{uuid}(刪除)、GET /watch/{uuid}/history(歷史快照)、POST /import(批量匯入)、GET /systeminfo(系統狀態)。
如果你要「偵測到變更就觸發後續動作」,除了 json:// webhook,也可以搭配 n8n 完整教學 做一條自動化流程,例如變更 → 寫入 Google Sheet → 發 Slack。
與其他方案比較
| 方案 | 自架 | 成本 | 選擇器/條件 | 通知管道 | 適合誰 |
|---|---|---|---|---|---|
| changedetection.io | ✅ | 免費(自架);官方 SaaS 約 $8.99/月 | CSS/XPath/JSONPath/jq/regex+條件+LLM | Apprise 100+ 種 | 想完全掌控、要監控大量頁面 |
| Distill.io | ❌(瀏覽器擴充+雲端) | 有免費額度,付費方案 | 視覺選取 | Email/Slack/Webhook | 不想自架、頁面數量少 |
| Visualping | ❌ | 有免費額度,付費方案 | 視覺選取 | Email/Slack | 非技術使用者 |
| 自己寫 cron + Playwright | ✅ | 免費 | 全部自己寫 | 自己接 | 工程師、需求高度客製 |
| RSS/官方降價通知 | ❌ | 免費 | 無 | 官方 | 網站本身有支援 |
選擇邏輯很簡單:要監控的頁面超過 10 個、或想自己掌握資料、或想接本機 LLM,就直接自架 changedetection.io。 只有偶爾盯一兩個頁面、又不想碰 Docker,才考慮 SaaS。
台灣實戰場景
- 電商降價與補貨:把商品頁丟進去、切到價格模式、設 3% 門檻,只在真的降價時收通知。(大型電商反爬較兇,若被擋,改用 Browser Steps 模擬正常瀏覽,並把檢查間隔拉長。)
- 政府/公部門公告:招標公告、法規異動、榜單、開課名額這類頁面通常有穩定 HTML 結構,用 CSS selector 鎖定列表區塊就很好用。
- 求職:盯公司
careers頁或職缺平台的關鍵字搜尋結果,新職缺一出現就通知。 - B2B 競品監控:盯競品定價頁與條款頁,改版當天就知道。
- PDF 改版:規格書、辦法、費率表這類 PDF 檔案直接丟進去,它會比對文字內容。
⚠️ 合規提醒:專案 README 的免責聲明寫得很清楚——你必須自行確保監控行為符合目標網站的服務條款、robots.txt 與當地法律。 實務上請把檢查間隔設得合理(不要 1 分鐘打人家一次)、不要繞過付費牆、不要把抓到的個資存下來。這工具是幫你省時間,不是幫你踩線。
7 個新手一定會踩的陷阱
- 沒設選擇器就開始監控 → 整頁任何風吹草動都通知你,三天內你就會關掉它。先設 CSS selector 或減法選擇器。
- JS 網站沒開 Chrome fetcher → 抓到空殼、或每次快照都一模一樣。看到「明明有改卻沒通知」就先檢查 fetcher 對不對。
- 直接綁
0.0.0.0開公網 → 你的監控清單與 API key 門戶洞開。請走反向代理+密碼保護。 - 檢查間隔設太短 → 被目標網站封 IP、或自己 VPS 被鄰居投訴。設
MINIMUM_SECONDS_RECHECK_TIME當保險,預設至少 15 分鐘起。 - 把價格寫死在選擇器裡 → 前端一改 class 就壞掉。優先用 JSON-LD/
json:$..price。 - 忘了 AI 功能會外傳內容 → 若監控的是內部或含敏感資料的頁面,請改接本機 Ollama,或直接
LLM_FEATURES_DISABLED=true。 - 更新容器時砍掉 volume → 所有 watch、快照與歷史都再見。更新只要
docker compose pull && docker compose up -d,不要加-v。
備份與升級
資料全都住在 Docker volume 裡的 /datastore。備份就是打包它:
docker run --rm \
-v changedetection-data:/data \
-v "$PWD":/backup \
alpine tar czf /backup/changedetection-backup-$(date +%F).tar.gz -C /data .
升級(保留資料):
docker compose pull
docker compose up -d
原始來源
- changedetection.io 官方網站
- dgtlmoon/changedetection.io — GitHub(README 與 docker-compose.yml)
- changedetection.io REST API 文件(OpenAPI 規格)
- Apprise 通知服務清單
- changedetection.io Wiki(Playwright、Proxy、反向代理設定)
