刷新商品页面并手动将数字复制到电子表格,只要追踪的商品超过寥寥几件,这种做法就行不通了。它慢、容易遗忘,更糟糕的是,它只告诉你价格现在是多少,却对这个数字是否移动、移动了多少、是否有意义毫无帮助。商品数据中真正有价值的信号是变化,而不是快照,只有定期采集相同字段并随时间比较,你才能看到变化。

本指南将用 Python 构建一个小巧、可运行的 AI 商品监控工具。它通过 Crawlbase 定时抓取公开商品页面,存储每次读数,将新读数与上一次进行对比以发现价格、库存和评分的显著变动,然后将这些变动交给 LLM 写成简短的白话文告警。这里所有内容都限定在公开商品数据范围内:任何访客无需登录即可看到的价格、库存状态和评分。无账号、无购物车、无个人数据。

工具的端到端运作方式

将这个系统想象成一条有四个环节的接力链。首先,Crawlbase 抓取商品页面并返回干净的结构化字段,省去手动解析脆弱 HTML 的麻烦。其次,每次读数附带时间戳写入本地存储,提供变化检测所依赖的历史记录。第三,对比步骤将最新读数与上一次进行比较,判断是否有值得关注的变动。第四,当发现真实变化时,LLM 将原始的前后数字转化为可以直接放进 Slack、邮件或日志的一行摘要。

调度循环将四个环节包裹在一起,使整个系统每小时或每天自动运行,无需人工干预。这个循环正是将一次性抓取变成真正监控的关键。同样的架构支撑着大多数电商网页抓取工作:采集、存储、比较、行动。

前提条件

你不需要是专家,但一些基础知识有帮助。你应该能读懂和编辑 Python 脚本,能发送 HTTP 请求并查看返回的 JSON,能从终端运行文件。对于摘要步骤,大致了解 LLM 如何响应结构化提示词是有益的,尽管代码会帮你处理连接部分。

工具方面你需要三样东西:本地安装的 Python 3.9 或更高版本,带有 API token 的 Crawlbase 账号,以及用于摘要步骤的 LLM API key(示例使用 OpenAI 兼容的端点,大多数提供商都支持这种接口)。新 Crawlbase 账号包含 1,000 次免费请求,在消耗之前添加账单信息可解锁额外的 9,000 次,足以针对几件真实商品完成构建和测试。

搭建项目

创建文件夹、虚拟环境,并安装工具所依赖的两个库:用于抓取的 Crawlbase 客户端,以及用于摘要步骤的 openai 客户端(它可以与任何 OpenAI 兼容的 API 通信)。

bash
python --version

mkdir product-monitor && cd product-monitor
python -m venv .venv
source .venv/bin/activate
pip install crawlbase openai

将两个 key 保存在代码之外。从环境变量读取,避免任何机密内容进入提交。在运行任何内容之前,在你的 shell 中设置一次。

bash
export CRAWLBASE_TOKEN="your_crawlbase_token"
export LLM_API_KEY="your_llm_api_key"

第一步:用 Crawlbase 采集商品数据

第一个环节抓取商品页面并返回我们关心的字段。对于受支持的商店,最简洁的路径是 Crawling API,它在服务端运行维护好的解析器并直接返回结构化 JSON,而不是需要自己处理的原始 HTML。你调用与 Crawling API 相同的端点,并添加 scraper 参数指定所需的解析器。将此保存为 collect.py

python
import os
from crawlbase import ScraperAPI

scraper = ScraperAPI({"token": os.environ["CRAWLBASE_TOKEN"]})

def collect_product(url):
    # 'amazon-product-details' is one of the maintained parsers.
    response = scraper.get(url, {"scraper": "amazon-product-details"})
    body = response["json"]["body"]

    reading = {
        "url": url,
        "name": body.get("name"),
        "price": body.get("rawPrice"),
        "currency": body.get("currency"),
        "in_stock": body.get("inStock"),
        "rating": body.get("rating"),
    }

    if reading["price"] is None or reading["name"] is None:
        raise ValueError(f"Parse returned no price/name for {url}")

    return reading

如果你追踪的商店不在受支持的解析器之列,可以退而使用 Crawling API 自行解析 HTML,或者生成一个针对目标的自定义提取器。无论哪种方式,Crawlbase 都处理请求中最难的部分:它轮换 IP、管理请求头,并在页面需要时渲染 JavaScript,因此你获得的是真实响应而不是封锁页面。

普通渲染与 JS 渲染

Scraper API 和 Crawling API 默认使用快速静态抓取。如果商品页面在客户端渲染价格或库存(现代电商平台很常见),请传入 "ajax_wait": "true""page_wait"(毫秒数),让内容在返回 HTML 之前完成加载。从 5000 毫秒开始,如果字段仍然为空就继续增加。

第二步:附带时间戳存储每次读数

变化检测需要记忆,因此每次读数都要附带采集时间写入磁盘。一个 SQLite 文件就足够了,并且能保持整个工具对依赖的轻量化。将此保存为 store.py

python
import sqlite3
from datetime import datetime, timezone

DB = "readings.db"

def init_db():
    con = sqlite3.connect(DB)
    con.execute(
        """CREATE TABLE IF NOT EXISTS readings (
            url TEXT, name TEXT, price REAL, currency TEXT,
            in_stock INTEGER, rating REAL, taken_at TEXT)"""
    )
    con.commit()
    con.close()

def save_reading(r):
    con = sqlite3.connect(DB)
    con.execute(
        "INSERT INTO readings VALUES (?, ?, ?, ?, ?, ?, ?)",
        (r["url"], r["name"], r["price"], r["currency"],
         int(bool(r["in_stock"])), r["rating"],
         datetime.now(timezone.utc).isoformat()),
    )
    con.commit()
    con.close()

def last_two(url):
    con = sqlite3.connect(DB)
    con.row_factory = sqlite3.Row
    rows = con.execute(
        "SELECT * FROM readings WHERE url = ? ORDER BY taken_at DESC LIMIT 2",
        (url,),
    ).fetchall()
    con.close()
    return [dict(row) for row in rows]

last_two 返回最新读数和上一次读数,这正是对比步骤所需要的全部内容。如果你之后想要绘制完整价格历史图表,表格已经存储了所有记录;只需按 taken_at 排序查询某个 URL 的所有记录即可。

第三步:检测有意义的变化

大多数简单监控器在这里出错:它们对每次微小波动都发出告警,让你在一天内就将其屏蔽。解决方案是设置阈值。只有当价格变动超过你设定的百分比时才视为变化,同时对二元状态(如商品缺货)始终发出告警。将此保存为 detect.py

python
PRICE_THRESHOLD = 0.03  # 3% move counts as meaningful

def detect_changes(current, previous):
    changes = []

    old_price, new_price = previous["price"], current["price"]
    if old_price and new_price:
        delta = (new_price - old_price) / old_price
        if abs(delta) >= PRICE_THRESHOLD:
            changes.append({
                "field": "price",
                "old": old_price,
                "new": new_price,
                "pct": round(delta * 100, 1),
            })

    if previous["in_stock"] != current["in_stock"]:
        changes.append({
            "field": "in_stock",
            "old": bool(previous["in_stock"]),
            "new": bool(current["in_stock"]),
        })

    if previous["rating"] and current["rating"]:
        if abs(current["rating"] - previous["rating"]) >= 0.2:
            changes.append({
                "field": "rating",
                "old": previous["rating"],
                "new": current["rating"],
            })

    return changes

根据商品调整阈值。整天波动几分钱的大宗商品需要更宽的价格区间;高价商品中 3% 的下跌才是真金白银,可以设置更紧的阈值。关键在于规则以可读可调的代码形式存在,而不是埋在模型的判断中。

Crawlbase Crawling API

监控工具的可靠性取决于提供数据的质量。Scraper API 从受支持的商店返回干净的结构化商品字段,IP 轮换、请求头管理和 JavaScript 渲染均在服务端处理,因此计划任务能持续返回真实读数而不是封锁页面。在免费层对公开商品页面进行测试,然后围绕它构建循环。

第四步:用 LLM 生成摘要和告警

变化字典列表是准确的,但一眼扫不出重点。LLM 在这里的职责是有限且刻意为之的:将结构化变化转化为一句简短、准确的话。将模型限制在紧凑、结构化的输入上,是防止其漂移或捏造细节的关键。将此保存为 alert.py

python
import os
import json
from openai import OpenAI

client = OpenAI(api_key=os.environ["LLM_API_KEY"])

def summarize_changes(product_name, changes):
    prompt = (
        f"Product: {product_name}\n"
        f"Detected changes (JSON): {json.dumps(changes)}\n\n"
        "Write one short sentence summarizing what changed. "
        "State only what the data shows. Do not speculate or "
        "add numbers that are not present."
    )

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system",
             "content": "You summarize product data changes factually."},
            {"role": "user", "content": prompt},
        ],
        temperature=0.1,
    )
    return response.choices[0].message.content.strip()

def send_alert(message):
    # Swap this for Slack, email, or a webhook in production.
    print(f"[ALERT] {message}")

temperature 和"只陈述数据所显示的内容"的指令,让摘要始终与你传入的数字保持一致。如果你不想依赖托管模型,可以通过更改客户端的基础 URL 将相同调用路由到自托管或替代的 OpenAI 兼容端点;函数的其余部分不变。要真正发送告警,将 send_alert 中的 print 替换为 Slack webhook POST 或邮件发送。

第五步:将循环串联起来

现在将四个环节连接成一次对监控列表的完整遍历。每次运行采集最新读数,保存,与上一次读数比较,只有在对比发现变化时才发出告警。将此保存为 monitor.py

python
from collect import collect_product
from store import init_db, save_reading, last_two
from detect import detect_changes
from alert import summarize_changes, send_alert

WATCHLIST = [
    "https://www.example-store.com/product/abc",
    "https://www.example-store.com/product/xyz",
]

def run_once():
    init_db()
    for url in WATCHLIST:
        try:
            reading = collect_product(url)
        except Exception as exc:
            print(f"Skipped {url}: {exc}")
            continue

        save_reading(reading)
        history = last_two(url)
        if len(history) < 2:
            continue  # first reading, nothing to compare

        current, previous = history[0], history[1]
        changes = detect_changes(current, previous)
        if changes:
            summary = summarize_changes(reading["name"], changes)
            send_alert(summary)

if __name__ == "__main__":
    run_once()

运行两次(中间间隔一段时间,或预先向表格中填入两条读数),当有内容发生变化时你就会看到告警触发。对稳定商品的单次运行不输出任何内容,这正是你想要的:无消息才是好消息。

bash
python monitor.py
# [ALERT] The price of "Acme Widget" dropped 7.4% from $129 to $119, and it is back in stock.

第六步:安排定时运行

监控意味着在定时器触发时无需你手动输入命令。不要在 Python 进程内部无限循环并调用 sleep;那样一旦机器重启就会停止运行,也不会留下任何日志。改为将调度交给操作系统。在 Linux 或 macOS 上,每小时运行一次脚本的 cron 条目如下。

bash
# crontab -e, then add (runs at the top of every hour):
0 * * * * cd /path/to/product-monitor && .venv/bin/python monitor.py >> monitor.log 2>&1

在 Windows 上,Task Scheduler 完成同样的工作:将基本任务指向虚拟环境内的 Python 可执行文件,以 monitor.py 为参数,并将触发器设置为你选择的间隔。无论哪种方式,选择与数据实际变化速度相匹配的频率。每小时适合快速变动的价格;对于库存和评分,每天一次已经足够,而且对你的请求额度也更友好。

随着监控列表增长到几件商品以上,逐一同步获取 URL 的循环开始变得缓慢。此时将采集迁移到异步 Crawler,它在页面完成时通过 webhook 推送结果,你无需在每个请求上阻塞。有关随时间追踪竞争对手价格的更广泛策略,请参阅价格情报中的网页抓取;有关同一管道在大规模下如何保持稳定,请参阅大规模电商抓取

保持数据持续流入

计划任务抓取器是一个必须在无人值守下持续工作的抓取器,因此数据层是可靠性最重要的地方。Crawlbase 已经在每次请求背后轮换 IP 并管理请求头,这是让重复性任务不被标记为机器人的关键。如果你需要对路由进行更精细的控制,或者想通过轮换池发送自己的 HTTP 客户端,Smart AI Proxy 将相同的网络作为标准代理端点公开。关注你的运行日志中偏离成功的状态码:突然出现的一连串挑战或错误,是放慢节奏或扩大轮换的信号,而不是更努力重试的信号。

回顾

核心要点

  • 监控变化,而非快照。价值在于随时间比较读数,这意味着你必须附带时间戳存储每次读数。
  • Crawlbase 是可靠的数据层。Crawling API 返回干净的结构化字段,IP 轮换加渲染让计划任务不会被封锁。
  • 用阈值检测有意义的变动。对价格设置百分比区间,加上对库存的二元检查,让告警成为有效信号而非噪声。
  • 让 LLM 保持专注。传入结构化对比结果,要求以低温度生成一句陈述性话语,它就会进行摘要而不是捏造内容。
  • 让操作系统来调度。Cron 或 Task Scheduler 优于进程内 sleep 循环;选择与数据变化速度相匹配的节奏。
  • 只使用公开数据。仅限价格、库存和评分;无账号、无购物车、无个人数据。

常见问题

什么是 AI 商品监控工具?

它是一个按计划监控公开商品页面的程序,每次记录价格、库存和评分等关键字段,并使用 AI 模型标记和解释有意义的变化。抓取层保持数据稳定流入,AI 层将原始的前后数字转化为简短可读的告警,让你对变动而非电子表格采取行动。

这个工具需要 Crawling API 还是 Scraper API?

当你追踪的商店是受维护的解析器之一时,使用 Scraper API,因为它直接返回结构化商品字段,省去编写提取代码的麻烦。当你需要自行解析原始 HTML 或页面没有对应解析器时,使用 Crawling API。两者共享同一网络,因此 IP 轮换和渲染在两种情况下都有效;区别仅在于 Crawlbase 是否为你解析页面。

工具如何判断什么算作有意义的变化?

通过你在普通代码中设置的阈值,而不是模型的判断。示例中只有价格变动超过百分比区间(默认 3%)才视为变化,商品库存状态变动始终标记,评分变化 0.2 或以上也会标记。根据商品调整这些数字,是保持告警有用而非频繁的方式。

AI 在写告警时会捏造数字吗?

开放式提示词存在这种风险,这也是为什么摘要步骤要保持紧凑。模型只接收结构化对比结果,以低温度运行,并被明确要求只陈述数据所显示的内容,不添加不在其中的数字。这种结构是让句子与你的实际读数而非虚构细节保持一致的关键。

商店改变页面布局时会发生什么?

如果你使用的是 Scraper API,维护好的解析器会为你吸收大多数布局变化,这也是值得在有支持的地方使用它的原因之一。如果你通过 Crawling API 自行解析 HTML,布局变化可能会破坏你的选择器,修复方法是重新检查实时页面并更新它们。由于 Crawlbase 始终返回完整页面,你调整的是解析逻辑,而不是重新构建请求。

监控应该多久运行一次?

让节奏与数据变化的速度和你的请求额度相匹配。每小时适合快速变动的价格;每天一次对于库存和评分已经足够,使用的请求数量也少得多。通过 cron 或 Windows Task Scheduler 调度,可以按任务设置间隔,如果某些商品比其他商品更重要,你可以以不同的速率运行不同的商品。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量