监控(Monitors)
按计划追踪网页与价格变化,支持 diff 检测、AI 过滤,以及 Webhook 或邮件告警。
简介
Monitors(监控)用于持续追踪 URL 的变化。每个 monitor 按 cron 计划执行抓取,将结果与上一快照对比,并在检测到有意义的变化时发送通知。
核心能力:网页文本 diff、结构化价格提取、可选 AI 判断、Webhook/邮件通知、快照历史、按需立即检查。
监控类型
| 类型 | monitor_type | 默认 track_mode | 适用场景 |
|---|---|---|---|
| 网页 | "webpage" | "text" | 文档、博客、服务条款、状态页 |
| 价格 | "price" | "json" | 商品价格、库存、结构化字段 |
Monitor 基于 定时任务(Scheduled Tasks) 实现。创建 monitor 时会自动创建一条 1:1 关联的后台 scrape 定时任务,调度由 monitor API 统一管理。
MVP 说明: 请求体可包含多个 targets,但当前仅对 第一个 target 创建调度。多 target 支持将在后续版本提供。
API 端点
POST /v1/monitors # 创建 monitor
GET /v1/monitors # 列出 monitors
GET /v1/monitors/:id # 获取详情
PATCH /v1/monitors/:id # 更新
DELETE /v1/monitors/:id # 删除
POST /v1/monitors/:id/pause # 暂停
POST /v1/monitors/:id/resume # 恢复
POST /v1/monitors/:id/check # 立即触发检查
GET /v1/monitors/:id/snapshots # 快照列表
GET /v1/monitors/:id/changes # 变更列表
GET /v1/monitors/:id/changes/:changeId # 变更详情快速开始
网页变更监控
每小时检查文档页,变更时通过 Webhook 告警:
curl -X POST "https://api.anycrawl.dev/v1/monitors" \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"name": "文档更新监控",
"monitor_type": "webpage",
"cron_expression": "0 * * * *",
"timezone": "Asia/Shanghai",
"targets": [
{
"url": "https://example.com/changelog",
"engine": "auto"
}
],
"diff_options": {
"only_main_content": true,
"min_change_ratio": 0.01
},
"notify_options": {
"channels": ["webhook"],
"only_meaningful": true
}
}'响应示例
{
"success": true,
"data": {
"monitor_id": "550e8400-e29b-41d4-a716-446655440000",
"scheduled_task_id": "660e8400-e29b-41d4-a716-446655440001",
"track_mode": "text",
"next_execution_at": "2026-07-17T13:00:00.000Z"
}
}价格监控
每 15 分钟提取并对比商品价格:
{
"name": "商品价格追踪",
"monitor_type": "price",
"cron_expression": "*/15 * * * *",
"targets": [
{ "url": "https://shop.example.com/product/12345", "engine": "auto" }
],
"extract_schema": {
"type": "object",
"properties": {
"price": { "type": "number" },
"currency": { "type": "string" },
"in_stock": { "type": "boolean" }
},
"required": ["price"]
},
"notify_options": {
"channels": ["webhook"],
"thresholds": { "price_change_pct": 5 }
}
}当 monitor_type 为 "price" 时,必须提供 extract_schema。
请求参数
核心配置
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
name | string | 是 | - | 名称(1–255 字符) |
monitor_type | string | 否 | "webpage" | "webpage" 或 "price" |
cron_expression | string | 是 | - | 标准 5 段 cron |
timezone | string | 否 | "UTC" | 时区 |
targets | array | 是 | - | 监控目标 URL 列表 |
goal | string | 否 | - | AI 判断用的自然语言目标 |
track_mode | string | 否 | 自动推断 | "text" / "json" / "mixed" |
extract_schema | object | 条件必填 | - | 结构化提取 schema(price 必填) |
diff_options
| 参数 | 说明 |
|---|---|
only_main_content | 仅对比正文,默认 true |
ignore_selectors | 排除包含指定字面短语的文本行;不是 CSS 选择器 |
min_change_ratio | 最小变更比例(0–1) |
notify_options
| 参数 | 说明 |
|---|---|
channels | "webhook" 和/或 "email" |
email_recipients | 启用 email 时必填 |
only_meaningful | 过滤噪声,默认 true |
thresholds.price_change_pct | 价格变更百分比阈值 |
邮件通知需在自托管环境中配置 ANYCRAWL_SMTP_*,详见 Docker 部署。
变更检测流程
- 抓取 — 后台定时任务执行 scrape
- 标准化 — 提取正文、应用字面文本行排除
- 快照 — 存储内容与 hash
- Diff — 文本 diff 或 JSON 字段对比
- 判断 — 设置
goal时由 AI 过滤噪声 - 通知 — 发送 Webhook 或邮件
管理 Monitor
// 生命周期
await client.pauseMonitor(monitorId);
await client.resumeMonitor(monitorId);
await client.runMonitor(monitorId); // 立即检查
// 历史
const snapshots = await client.getMonitorSnapshots(monitorId, { limit: 20 });
const changes = await client.getMonitorChanges(monitorId, { limit: 20 });
const detail = await client.getMonitorChange(monitorId, changeId);立即检查返回 202;若已有检查进行中则返回 409。
Webhook 事件
通过 Webhooks 订阅:
| 事件 | 说明 |
|---|---|
monitor.check.completed | 检查完成(含摘要) |
monitor.changed | 网页内容有意义变更 |
monitor.price.changed | 价格或结构化字段变更 |
monitor.error | 检查失败 |
Payload 内联 diff 数据,无需额外请求 changes API。
JavaScript SDK
安装 0.0.6+ 版本:
pnpm add @anycrawl/js-sdk完整 API 见英文文档 Monitors 或 SDK README。
限制
| 项 | 限制 |
|---|---|
| 实际调度的 target 数 | 1(MVP 仅首个) |
| 请求体 targets 上限 | 50 |
| 邮件收件人 | 20 |
| 内联快照大小 | 默认 256 KB(ANYCRAWL_MONITOR_MAX_INLINE_CHARS) |
相关文档
检查恢复、状态与分页
每次检查在 DB 中记录 pending → ready → processing → completed/failed,抓取终态与后处理意图一起提交。Worker 用 lease 与 fencing 恢复失败处理;快照、变化、通知意图及处理终态原子提交。同一 monitor 在抓取和后处理期间只有一个活跃检查,skip 跳过重叠 cron,queue 延后;手动检查也遵守单飞。监控不占普通 scheduled-task 配额。
Monitor GET 返回 in_progress、last_check_state、last_check_at、last_check_error、pause_reason 和 revision。有效运行状态为 is_active && !is_paused;自动暂停直接 resume。409 的 code 分为 MONITOR_PAUSED 与 MONITOR_CHECK_IN_PROGRESS,不能统一当作“正在检查”。
PATCH 在事务内合并并验证最终配置。嵌套 options 保留未提交的兄弟字段;goal=null 清除说明及抽取 prompt;price/json/mixed 必须保留可用 schema。monitor_type 不支持修改,未知顶层字段返回 400,target options 不支持模板。有效抓取、模式、goal 或文本排除规则变化会开始新 revision 的基线;仅通知/阈值变化保留原基线。复杂 JSON schema 在请求和 UI 编辑过程中保留原结构。
首次无效抽取返回 error,不建立健康基线。完整正文保存在数据库;快照详情 /v1/monitors/:id/snapshots/:snapshotId 返回预览,含 content_truncated/content_length/content_complete/monitor_revision。预览默认 262144 字符,比较正文上限默认 2000000 字符;超过比较上限是错误,不是 same。旧不完整正文保留历史,但不参与新基线。
快照、单 monitor changes 和 /v1/monitors/changes 返回 {success,data,pagination:{has_more,next_cursor}}。下一页使用服务器 cursor,offset 仍兼容;时间相同时按 uuid 排序。changes 可传 include_diff_text=false,展开时读取单条详情。
notified 仅在至少一个 SMTP 收件人被接受或 Webhook HTTP 2xx 后为 true。notification_status 区分 none/pending/queued/delivered/failed/skipped;legacy 的历史 boolean 无法证明送达。/v1/monitors/:id/checks 返回检查历史,/v1/monitors/:id/notifications 返回按渠道/收件人的状态与错误,change detail 也包含 notifications。错误检查同样可以发 email;SMTP 未配置或临时失败会有限重试并保留原因。稳定 Delivery-ID/Message-ID 支持识别重试,但不保证邮件 exactly-once。
AI 判断服务不可用或组合 diff 输入不完整时记录 meaningful=null、status=unavailable/incomplete,保留变化证据。固定 country routing 不支持,capabilities.location=false;不能用 location 字段证明真实出口。价格图按 URL/path/currency 分组,并说明只展示已加载的变化点。
恢复 Worker 随 scheduler 启动,也可单独 --queues=monitor。默认 ANYCRAWL_MONITOR_MAX_ATTEMPTS=5、RETRY_DELAY_MS=5000、LEASE_MS=120000、POLL_MS=5000(后三者均以 ANYCRAWL_MONITOR_ 为前缀)。ANYCRAWL_MONITOR_RETENTION_DAYS 默认 0;显式启用后分批清理新工作流历史,保护健康基线、保留引用、待送达记录及 legacy。升级不自动补发旧告警。
const feed = await client.listMonitorChanges({ limit: 20 });
const page = await client.getMonitorSnapshotsPage(monitorId, { limit: 20 });
if (page.data[0]) await client.getMonitorSnapshot(monitorId, page.data[0].uuid);
const checks = await client.getMonitorChecks(monitorId);
const notifications = await client.getMonitorNotifications(monitorId);