🚀

用 Python 做网站变化监控,改版第一时间收到通知

定期抓取网页截图并对比,发现页面变化自动发送告警。适合监控竞品网站、政策更新、价格变动等场景。

👁️ - 次阅读

你有没有遇到过这种情况:蹲点等一个降价通知,结果被别的渠道先知道了?

或者想监控竞品网站有没有改版、新功能上线,却只能每天手动去点一看?

这种时候,脚本比人眼靠谱。

今天做一个网站变化监控工具。它定期截图,对比前后画面,有变化就推送到你的微信或邮箱。

项目背景

很多场景需要盯住某个网页的变化:

  • 竞品网站改版、上新品、调价格
  • 政策文件更新、公告发布
  • 抢票网站余票变化
  • 自己网站的访问异常

手动刷新太累,专业工具又贵。用 Python 搭一个自己的监控器,成本为零。

核心思路很简单:

  1. 定时访问目标网页
  2. 截图保存
  3. 与上次截图对比像素差异
  4. 有变化就发通知

技术选型

组件选型理由
网页截图playwright支持无头浏览器,渲染完整页面
图像对比Pillow + numpy计算像素差异率
定时任务APSchedulerPython 内置调度,灵活
通知推送微信企业号/邮件可替换任意渠道
配置管理YAML简单直观,容易扩展

实现步骤

第一步:安装依赖

pip install playwright pillow numpy apscheduler pyyaml
playwright install chromium

第二步创建项目目录:

mkdir -p site_monitor/{screenshots,logs}
touch monitor.py config.yaml

第三步:编写配置文件 config.yaml

# 监控目标列表
targets:
  - url: "https://example.com"
    name: "示例网站"
    interval: 3600  # 每小时检查一次
    notify: true    # 有变化时通知
    
  - url: "https://competitor.com/pricing"
    name: "竞品价格页"
    interval: 7200  # 每两小时检查
    notify: true

# 通知设置
notification:
  type: "wechat"  # wechat / email / webhook
  wechat:
    webhook_url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY"
  email:
    smtp_server: "smtp.qq.com"
    smtp_port: 465
    sender: "your@qq.com"
    password: "your_password"
    receiver: "target@qq.com"

# 截图配置
screenshot:
  width: 1440
  height: 900
  compare_threshold: 0.05  # 像素差异超过5%才触发告警

第四步:编写核心监控脚本 monitor.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import yaml
import time
from datetime import datetime
from pathlib import Path
from PIL import Image
import numpy as np
from playwright.sync_api import sync_playwright
from apscheduler.schedulers.blocking import BlockingScheduler
import requests

# 加载配置
with open("config.yaml", "r", encoding="utf-8") as f:
    config = yaml.safe_load(f)

SCREENSHOT_DIR = Path("screenshots")
SCREENSHOT_DIR.mkdir(exist_ok=True)

def take_screenshot(url, name, width=1440, height=900):
    """抓取网页截图"""
    filename = f"{SCREENSHOT_DIR}/{name}_{int(time.time())}.png"
    
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page(viewport={"width": width, "height": height})
        page.goto(url, wait_until="networkidle")
        page.screenshot(path=filename, full_page=True)
        browser.close()
    
    return filename

def compare_screenshots(old_path, new_path, threshold=0.05):
    """对比两张截图,返回差异率"""
    img1 = np.array(Image.open(old_path).convert("L"))
    img2 = np.array(Image.open(new_path).convert("L"))
    
    # 调整尺寸到一致
    if img1.shape != img2.shape:
        img2 = np.array(Image.fromarray(img2).resize((img1.shape[1], img1.shape[0])))
    
    # 计算像素差异率
    diff = np.abs(img1.astype(float) - img2.astype(float)) / 255.0
    diff_rate = np.mean(diff)
    
    return diff_rate > threshold, diff_rate

def send_wechat_notification(url, name, diff_rate):
    """发送微信通知"""
    webhook = config["notification"]["wechat"]["webhook_url"]
    message = f"🔔 网站变化监控\n\n网站:{name}\n链接:{url}\n变化率:{diff_rate:.2%}\n时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
    
    payload = {
        "msgtype": "text",
        "text": {
            "content": message
        }
    }
    
    requests.post(webhook, json=payload, timeout=10)
    print(f"✅ 已发送微信通知:{name}")

def check_site(target):
    """检查单个网站是否有变化"""
    name = target["name"]
    url = target["url"]
    notify = target.get("notify", True)
    
    print(f"\n🔍 正在检查:{name} ({url})")
    
    # 获取最新截图
    latest = take_screenshot(url, name, 
                             config["screenshot"]["width"],
                             config["screenshot"]["height"])
    
    # 找上一次截图
    screenshots = sorted(SCREENSHOT_DIR.glob(f"{name}_*.png"))
    
    if len(screenshots) < 2:
        print(f"  ⏳ 首次监控,已保存截图")
        return
    
    old_path = screenshots[-2]
    new_path = latest
    
    # 删除旧格式截图(避免混淆)
    for s in screenshots[:-2]:
        s.unlink()
    
    # 对比
    changed, diff_rate = compare_screenshots(old_path, new_path,
                                             config["screenshot"]["compare_threshold"])
    
    if changed:
        print(f"  ⚠️ 检测到变化!差异率:{diff_rate:.2%}")
        if notify:
            send_wechat_notification(url, name, diff_rate)
    else:
        print(f"  ✅ 无变化(差异率:{diff_rate:.2%})")
    
    # 清理本次截图(保留最新一份)
    latest.unlink(missing_ok=True)

def main():
    """主程序"""
    scheduler = BlockingScheduler()
    
    for target in config["targets"]:
        scheduler.add_job(
            check_site,
            "interval",
            seconds=target["interval"],
            args=[target],
            id=target["name"],
            misfire_grace_time=60
        )
        print(f"📌 已添加监控任务:{target['name']} (每{target['interval']}秒)")
    
    print("\n🚀 网站变化监控启动,按 Ctrl+C 退出\n")
    
    try:
        scheduler.start()
    except KeyboardInterrupt:
        print("\n👋 监控已停止")

if __name__ == "__main__":
    main()

第五步:运行监控

python monitor.py

启动后,程序会按配置的间隔定期检查所有目标网站。有变化时会输出日志并发送微信通知。

运行效果

📌 已添加监控任务:示例网站 (每3600秒)
📌 已添加监控任务:竞品价格页 (每7200秒)

🚀 网站变化监控启动,按 Ctrl+C 退出

🔍 正在检查:示例网站 (https://example.com)
  ✅ 无变化(差异率:0.02%)

🔍 正在检查:竞品价格页 (https://competitor.com/pricing)
  ⚠️ 检测到变化!差异率:12.35%
  ✅ 已发送微信通知:竞品价格页

微信收到通知:

🔔 网站变化监控

网站:竞品价格页
链接:https://competitor.com/pricing
变化率:12.35%
时间:2026-08-04 10:23:15

优化方向

  1. 智能对比:目前是全图像素对比,可以加入 OCR 识别文字变化,过滤掉广告位变动等干扰
  2. 变化区域标注:在截图上高亮显示差异区域,一眼看出改了什么
  3. 历史记录查询:把每次截图存入数据库,支持按时间轴回溯查看
  4. 浏览器缓存处理:有些网站会做 A/B 测试,可以加入随机 User-Agent 减少误判
  5. 多渠道通知:除了微信,还支持邮件、钉钉、飞书、Telegram
  6. Web 控制面板:做一个简单的 Web 界面,查看历史截图、添加/删除监控目标

总结

这个工具的核心价值在于"被动变主动"。

你不需要每天盯着网页看,脚本替你盯着。有变化了再通知你,效率翻倍。

监控对象越多,脚本越值钱。

现在就去加几个你想盯的网站,看看能发现什么。

这篇文章对你有帮助吗?