刷新商品页面并手动将数字复制到电子表格,只要追踪的商品超过寥寥几件,这种做法就行不通了。它慢、容易遗忘,更糟糕的是,它只告诉你价格现在是多少,却对这个数字是否移动、移动了多少、是否有意义毫无帮助。商品数据中真正有价值的信号是变化,而不是快照,只有定期采集相同字段并随时间比较,你才能看到变化。
本指南将用 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 通信)。
python --version mkdir product-monitor && cd product-monitor python -m venv .venv source .venv/bin/activate pip install crawlbase openai
将两个 key 保存在代码之外。从环境变量读取,避免任何机密内容进入提交。在运行任何内容之前,在你的 shell 中设置一次。
export CRAWLBASE_TOKEN="your_crawlbase_token" export LLM_API_KEY="your_llm_api_key"
第一步:用 Crawlbase 采集商品数据
第一个环节抓取商品页面并返回我们关心的字段。对于受支持的商店,最简洁的路径是 Crawling API,它在服务端运行维护好的解析器并直接返回结构化 JSON,而不是需要自己处理的原始 HTML。你调用与 Crawling API 相同的端点,并添加 scraper 参数指定所需的解析器。将此保存为 collect.py。
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,因此你获得的是真实响应而不是封锁页面。
Scraper API 和 Crawling API 默认使用快速静态抓取。如果商品页面在客户端渲染价格或库存(现代电商平台很常见),请传入 "ajax_wait": "true" 和 "page_wait"(毫秒数),让内容在返回 HTML 之前完成加载。从 5000 毫秒开始,如果字段仍然为空就继续增加。
第二步:附带时间戳存储每次读数
变化检测需要记忆,因此每次读数都要附带采集时间写入磁盘。一个 SQLite 文件就足够了,并且能保持整个工具对依赖的轻量化。将此保存为 store.py。
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。
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% 的下跌才是真金白银,可以设置更紧的阈值。关键在于规则以可读可调的代码形式存在,而不是埋在模型的判断中。
监控工具的可靠性取决于提供数据的质量。Scraper API 从受支持的商店返回干净的结构化商品字段,IP 轮换、请求头管理和 JavaScript 渲染均在服务端处理,因此计划任务能持续返回真实读数而不是封锁页面。在免费层对公开商品页面进行测试,然后围绕它构建循环。
第四步:用 LLM 生成摘要和告警
变化字典列表是准确的,但一眼扫不出重点。LLM 在这里的职责是有限且刻意为之的:将结构化变化转化为一句简短、准确的话。将模型限制在紧凑、结构化的输入上,是防止其漂移或捏造细节的关键。将此保存为 alert.py。
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。
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()
运行两次(中间间隔一段时间,或预先向表格中填入两条读数),当有内容发生变化时你就会看到告警触发。对稳定商品的单次运行不输出任何内容,这正是你想要的:无消息才是好消息。
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 条目如下。
# 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 次请求免费,无需信用卡。

