
Ethan Collins
Pattern Recognition Specialist

如果你曾经尝试过抓取价格、产品数据或受保护页面内容,你就知道困难之处不仅在于加载URL。工作流还必须解决网站的验证码,以网站期望的方式提交解决后的令牌,然后从受保护的响应中提取正确的数据。
这就是为什么简单的“解决验证码即可”示例不足以实现真正的自动化。一个网站可能期望令牌出现在头部、表单正文、JSON负载、查询字符串、Cookie或其他特定于应用的字段中。它可能使用reCAPTCHA、Turnstile或其他完全不同的验证码挑战。一旦受保护的响应返回,选择器和输出逻辑仍需与你的目标相匹配。
在本指南中,你将学习如何使用CapSolver在n8n中构建受验证码保护的网站抓取器。主要的演示流程基于仓库工作流 Scraping — Price & Product Details — CapSolver + Schedule + Webhook,但相同的模板可以适应以下场景:
本文内容是关于在你拥有、管理或被允许测试的目标上进行的实用、授权自动化。
重要提示: 这些工作流是示例和起点模板,不是通用的直接使用配方。你应预期需要修改验证码设置、令牌提交方式、请求负载、头部、Cookie、提取选择器和输出逻辑,以匹配每个特定网站。
本文中的主要示例是一个固定目标抓取器模板,现在支持两种激活模式:
在默认的仓库模板中,该工作流:
x-recaptcha-token头部提交令牌price和productName$workflow.staticData进行比较同样的模式可以变成:
这些仓库模板都遵循相同的可重用骨架:
触发 -> 解决验证码 -> 提交受保护请求 -> 提取结果 -> 比较/存储/输出
该结构适用于多种合法使用场景:
| 使用场景 | 什么会变化 |
|---|---|
| 抓取 | 提取价格字段并随时间比较它们 |
| 产品数据抓取 | 提取标题、SKU、卖家、库存或描述等字段 |
| 库存检查 | 比较可用性文本、数量或购买按钮状态 |
| 登录你的账户 | 将解决的令牌与你的登录请求一起提交并验证登录成功 |
| 受保护内容获取 | 获取受限制的内容并返回提取的字段 |
| Webhook触发的抓取 | 让其他服务按需激活固定配置的目标 |
结构保持可重用,但实际实现细节在每个网站上可能不同。实际上,用户应将每个工作流视为示例,然后根据目标网站调整解决配置、请求结构、令牌位置和提取逻辑。
上述主要抓取工作流获取原始页面内容并比较价格。以下工作流扩展了相同的验证码解决模式——触发 → 解决验证码 → 提交受保护请求 → 评估结果——以特定使用场景。每个都需要相同的先决条件:一个n8n实例、一个CapSolver凭证以及目标网站的验证码参数。
| 工作流 | 目的 |
|---|---|
Scraping — Price & Product Details — CapSolver + Schedule + Webhook |
固定目标的定时 + Webhook模板,解决reCAPTCHA v3,通过x-recaptcha-token提交令牌,提取price和productName,与$workflow.staticData比较,可用于抓取、产品详情提取或类似的受保护产品页面检查 |
激活说明: 该模板导入时为
active: false。Webhook路径在你配置占位符、选择CapSolver凭证并激活n8n中的工作流之前不会生效。
在开始之前,请确保你已具备以下条件:
对于本文中的主要示例,假设目标使用reCAPTCHA,所以关键值为:
websiteURLwebsiteKeypageAction(用于reCAPTCHA v3)在仓库的价格监控模板中,CapSolver节点配置为:
operation: reCAPTCHA v3websiteURL: https://YOUR-TARGET-SITE.com/product-pagewebsiteKey: YOUR_SITE_KEY_HEREpageAction: view_product当你检查一个reCAPTCHA目标时,请验证:
pageAction重要提示: 这不是通用的验证码检测部分。如果目标使用了其他挑战类型——如Cloudflare Turnstile、Cloudflare Challenge、GeeTest、DataDome、AWS WAF或MTCaptcha——你需要更改CapSolver设置和提交解决后令牌的HTTP请求。
仓库工作流 Scraping — Price & Product Details — CapSolver + Schedule + Webhook 现在支持两种固定目标激活路径:
Every 6 Hours 用于定期检查Webhook Trigger 用于按需运行两种路径都使用相同的占位符和相同的抓取逻辑。Webhook版本只是以**Respond to Webhook**结束,这样调用者可以以JSON形式获取最终的警报或无变化负载。
定时路径使用以下节点:
Every 6 HoursSolve reCAPTCHA v3Fetch Product PageExtract DataCompare DataData Changed?Build AlertNo ChangeWebhook路径为相同的固定目标重复相同的逻辑:
Webhook TriggerSolve reCAPTCHA v3 [Webhook]Fetch Product Page [Webhook]Extract Data [Webhook]Compare Data [Webhook]Data Changed? [Webhook]Build Alert [Webhook]No Change [Webhook]Respond to Webhook当需要定期检查时(如抓取或库存检查)使用定时触发器。
当其他工作流、服务或应用程序需要按需激活相同配置的目标时使用Webhook。
模板使用:
| 设置 | 值 |
|---|---|
| 操作 | reCAPTCHA v3 |
websiteURL |
https://YOUR-TARGET-SITE.com/product-page |
websiteKey |
YOUR_SITE_KEY_HERE |
pageAction |
view_product |
解决后的令牌通过请求头部提交:
| 头部 | 值 |
|---|---|
user-agent |
浏览器风格的用户代理 |
x-recaptcha-token |
{{ $json.data.solution.gRecaptchaResponse }} |
这是工作流中最重要的细节之一。仓库模板不假设令牌始终在g-recaptcha-response中。在这个示例中,它被放在自定义头部中。
price和productNameHTML节点提取:
| 键 | CSS选择器 |
|---|---|
price |
.product-price, [data-price], .price |
productName |
h1, .product-title |
代码节点使用以下内容存储并比较值:
$workflow.staticData.lastPrice$workflow.staticData.lastChecked这使得工作流能够区分:
IF节点检查 {{ $json.changed }}。
如果价格变化,工作流进入 Build Alert。
如果没有变化,它进入 No Change。
在Webhook路径中,任一分支随后进入 Respond to Webhook。
尽管主要示例包含价格比较逻辑,但更实用的思考方式是将其视为一个带有比较逻辑的受保护产品页面抓取器模板。
可重用的部分包括:
这就是为什么相同的结构可以驱动:
这是在真实目标上最重要的部分。
对于大多数真实网站,你应该假设这里几乎一切都可调整:挑战类型、解决参数、令牌提交位置、请求体、Cookie、头部、选择器,甚至最终的成功标准。这些仓库工作流是可工作的模式示例,而不是适用于每个网站的固定配方。
仓库的使用场景工作流现在支持定时 + Webhook。
使用:
这些模板中的Webhook路径是固定目标激活器,而不是用于任意用户提供的目标的公共API。
主要的价格监控示例使用reCAPTCHA v3,但你的目标可能使用:
如果类型改变,你需要相应地更新解决步骤。
即使两个网站都使用reCAPTCHA,设置也可能不同。
你可能需要更改:
operation或任务类型websiteURLwebsiteKeypageAction抓取器模板将这些作为占位符配置字段暴露出来,因为它们预计会变化。
不要假设令牌总是提交到相同的位置。
该仓库已经展示了几种模式:
| 模式 | 仓库示例 |
|---|---|
| 请求头部 | Scraping — CapSolver + Schedule 使用x-recaptcha-token |
| 表单正文 | 登录流程通常在表单编码的正文使用g-recaptcha-response |
在真实网站上,解决后的令牌可能位于:
此仓库示例是一种提交模式,而不是通用模式。
受保护请求可能需要的不仅仅是验证码或挑战令牌。
你可能需要调整:
这在以下场景中尤其常见:
主要抓取器模板提取:
priceproductName但你可以替换为:
当前的价格监控代码比较数值,但相同模式可以调整为:
以下JSON是此仓库中**Scraping — Price & Product Details — CapSolver + Schedule + Webhook的当前可导入版本,包括定时 + Webhook**激活路径。
{
"nodes": [
{
"parameters": {
"content": "## 抓取 \u2014 价格与产品监控\n\n### 如何工作\n\n1. 通过定时或Webhook输入触发以开始价格监控。\n2. 解决reCAPTCHA以访问目标产品页面。\n3. 获取并提取产品页面的数据以进行进一步分析。\n4. 将新获取的数据与之前存储的数据进行比较以检测变化。\n5. 确定是否发生更改并准备警报。\n6. 通过指定渠道发送基于数据分析的响应。\n\n### 设置步骤\n\n- [ ] 在'Every 6 Hours'节点中配置定时触发器的间隔。\n- [ ] 在'Set Target Config [Schedule]'中设置目标网站详情。\n- [ ] 在'Solve reCAPTCHA v3'节点中使用API密钥配置reCAPTCHA求解器。\n- [ ] 在'Webhook Trigger'中设置Webhook URL和路径。\n- [ ] 在'Build Alert'节点中定义警报标准和目的地。\n\n### 自定义\n\n在'Extract Data'节点中调整提取模式以适应特定产品详情。",
"height": 896,
"width": 480
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1328,
-304
],
"id": "52c7808e-d2bc-4779-85e6-909a51066338",
"name": "便签"
},
{
"parameters": {
"content": "## 定时触发设置\n\n使用定时触发器每6小时初始化一次数据监控过程,并设置抓取的目标配置。",
"height": 320,
"width": 496,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-752,
-160
],
"id": "3c5cee67-552d-48ea-8717-7c5126269e2e",
"name": "便签1"
},
{
"parameters": {
"content": "## 定时抓取流程\n\n解决 reCAPTCHA,获取产品页面,提取数据并与之前的记录进行比较以识别变化,遵循定时触发器。",
"height": 496,
"width": 1680,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-144,
-304
],
"id": "d0315be2-111c-4893-bf42-2f2cc2eb186f",
"name": "便签2"
},
{
"parameters": {
"content": "## Webhook触发器设置\n\n通过传入的Webhook处理手动触发的数据监控并解决reCAPTCHA以继续。",
"height": 304,
"width": 816,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-768,
336
],
"id": "a78f1606-07fb-40fd-af82-e1dc9b766206",
"name": "便签3"
},
{
"parameters": {
"content": "## Webhook抓取流程\n\n通过获取产品页面、提取数据并确定变化来处理Webhook触发的请求。",
"height": 272,
"width": 1088,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
160,
320
],
"id": "1a677fd9-a3a8-404f-ba9a-2b087d7bfe11",
"name": "便签4"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-704,
0
],
"id": "sc-901",
"name": "每6小时"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cfg-001",
"name": "websiteURL",
"value": "https://YOUR-TARGET-SITE.com/product-page",
"type": "string"
},
{
"id": "cfg-002",
"name": "websiteKey",
"value": "YOUR_SITE_KEY_HERE",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-400,
0
],
"id": "sc-900",
"name": "设置目标配置 [定时]"
},
{
"parameters": {
"operation": "reCAPTCHA v3",
"websiteURL": "={{ $json.websiteURL }}",
"websiteKey": "={{ $json.websiteKey }}",
"optional": {
"pageAction": "view_product"
}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-96,
0
],
"id": "sc-902",
"name": "解决reCAPTCHA v3",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "CapSolver账户"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $('设置目标配置 [定时]').first().json.websiteURL }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "user-agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
},
{
"name": "x-recaptcha-token",
"value": "={{ $json.data.solution.gRecaptchaResponse }}"
}
]
},
"options": {
"response": {
"response": {}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
208,
0
],
"id": "sc-903",
"name": "获取产品页面"
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price"
},
{
"key": "productName",
"cssSelector": "h1, .product-title"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
512,
0
],
"id": "sc-904",
"name": "提取数据"
},
{
"parameters": {
"jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || '产品';\nconst parsePrice = (str) => { if (!str) return null; const match = str.match(/[\\d,]+\\.?\\d*/); return match ? parseFloat(match[0].replace(',', '')) : null; };\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\nreturn [{ json: { productName, currentPrice, previousPrice: previousPrice || '首次检查', changed, direction, diff: changed ? `$${diff}` : null, checkedAt: new Date().toISOString() } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
0
],
"id": "sc-905",
"name": "比较数据"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "if-1",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1104,
0
],
"id": "sc-906",
"name": "数据已更改?"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a1",
"name": "alert",
"value": "=价格 {{ $json.direction }} 用于 {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }}",
"type": "string"
},
{
"id": "a2",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
},
{
"id": "a3",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1392,
-192
],
"id": "sc-907",
"name": "生成警报"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "n1",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "n2",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
},
{
"id": "n3",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1392,
32
],
"id": "sc-908",
"name": "无变化"
},
{
"parameters": {
"httpMethod": "POST",
"path": "price-monitor",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-720,
464
],
"id": "sc-909",
"name": "Webhook触发器",
"webhookId": "sc-909-webhook",
"onError": "continueRegularOutput"
},
{
"parameters": {
"operation": "reCAPTCHA v3",
"websiteURL": "={{ $json.body.websiteURL }}",
"websiteKey": "={{ $json.body.websiteKey }}",
"optional": {
"pageAction": "={{ $json.body.pageAction || 'view_product' }}"
}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-96,
464
],
"id": "sc-910",
"name": "解决reCAPTCHA v3 [Webhook]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "CapSolver账户"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Webhook触发器').item.json.body.websiteURL }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "user-agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
},
{
"name": "x-recaptcha-token",
"value": "={{ $json.data.solution.gRecaptchaResponse }}"
}
]
},
"options": {
"response": {
"response": {}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
208,
432
],
"id": "sc-911",
"name": "获取产品页面 [Webhook]"
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price"
},
{
"key": "productName",
"cssSelector": "h1, .product-title"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
512,
432
],
"id": "sc-912",
"name": "提取数据 [Webhook]"
},
{
"parameters": {
"jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || '产品';\nconst parsePrice = (str) => { if (!str) return null; const match = str.match(/[\\d,]+\\.?\\d*/); return match ? parseFloat(match[0].replace(',', '')) : null; };\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\nreturn [{ json: { productName, currentPrice, previousPrice: previousPrice || '首次检查', changed, direction, diff: changed ? `$${diff}` : null, checkedAt: new Date().toISOString() } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
432
],
"id": "sc-913",
"name": "比较数据 [Webhook]"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "if-2",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1104,
432
],
"id": "sc-914",
"name": "数据已更改? [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a4",
"name": "alert",
"value": "=价格 {{ $json.direction }} 用于 {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }}",
"type": "string"
},
{
"id": "a5",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
},
{
"id": "a6",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1424,
384
],
"id": "sc-915",
"name": "生成警报 [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "n4",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "n5",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
},
{
"id": "n6",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1440,
592
],
"id": "sc-916",
"name": "无变化 [Webhook]"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1712,
512
],
"id": "sc-917",
"name": "响应Webhook"
}
],
"connections": {
"每6小时": {
"main": [
[
{
"node": "设置目标配置 [计划]",
"type": "main",
"index": 0
}
]
]
},
"设置目标配置 [计划]": {
"main": [
[
{
"node": "解决 reCAPTCHA v3",
"type": "main",
"index": 0
}
]
]
},
"解决 reCAPTCHA v3": {
"main": [
[
{
"node": "获取产品页面",
"type": "main",
"index": 0
}
]
]
},
"获取产品页面": {
"main": [
[
{
"node": "提取数据",
"type": "main",
"index": 0
}
]
]
},
"提取数据": {
"main": [
[
{
"node": "比较数据",
"type": "main",
"index": 0
}
]
]
},
"比较数据": {
"main": [
[
{
"node": "数据已更改?",
"type": "main",
"index": 0
}
]
]
},
"数据已更改?": {
"main": [
[
{
"node": "构建警报",
"type": "main",
"index": 0
}
],
[
{
"node": "无更改",
"type": "main",
"index": 0
}
]
]
},
"Webhook 触发": {
"main": [
[
{
"node": "解决 reCAPTCHA v3 [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"解决 reCAPTCHA v3 [Webhook]": {
"main": [
[
{
"node": "获取产品页面 [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"获取产品页面 [Webhook]": {
"main": [
[
{
"node": "提取数据 [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"提取数据 [Webhook]": {
"main": [
[
{
"node": "比较数据 [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"比较数据 [Webhook]": {
"main": [
[
{
"node": "数据已更改? [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"数据已更改? [Webhook]": {
"main": [
[
{
"node": "构建警报 [Webhook]",
"type": "main",
"index": 0
}
],
[
{
"node": "无更改 [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"构建警报 [Webhook]": {
"main": [
[
{
"node": "响应 Webhook",
"type": "main",
"index": 0
}
]
]
},
"无更改 [Webhook]": {
"main": [
[
{
"node": "响应 Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
配置占位符并激活工作流后,触发 Webhook 路径:
curl -X POST https://your-n8n-instance.com/webhook/price-monitor \
-H "Content-Type: application/json" \
-d '{}'
预期响应(首次检查):
{
"status": "no_change",
"currentPrice": "$29.99",
"checkedAt": "2026-03-11T08:00:00.000Z"
}
预期响应(价格变动):
{
"alert": "Widget Pro 价格下降:$39.99 → $29.99(-$10.00)",
"severity": "deal",
"checkedAt": "2026-03-11T14:00:00.000Z"
}
包含实际价格数据的响应表明整个流程已正常运行——验证码已解决、受保护页面已获取、数据已提取且比较逻辑已执行。
如果 CapSolver 返回了令牌但网站仍然阻止请求,问题通常不在于验证码的解决本身。常见原因包括:
pageAction 错误如果目标网站使用的是非 reCAPTCHA 挑战(如 Cloudflare Turnstile、Cloudflare Challenge、GeeTest、DataDome、AWS WAF 或 MTCaptcha),则主示例无法直接使用。你需要更新:
如果 HTML 节点未能提取你期望的字段:
仓库模板导入时处于非活动状态。直到你:
Webhook 路径将不会处于活动状态。
Build Alert 或认证成功/失败节点之后添加下游通知或存储节点。准备好开始了吗? 注册 CapSolver 并使用优惠码 OPENCLAW 在首次充值时获得额外 6% 的奖励!
主要观点很简单:解决验证码只是工作流中的一个步骤。真正的抓取仍需正确提交令牌、发送正确的请求格式,并提取对你用例重要的字段。
这也是这些模板应被视为示例的原因。不同的网站可能使用不同的验证码类型,期望令牌出现在其他位置,需要额外的 cookies 或字段,返回不同的响应格式,并要求不同的提取或验证逻辑。
此模板为你提供了以下起点:
它使用了广泛模式:
触发器 -> 解决验证码 -> 提交受保护请求 -> 提取或验证结果 -> 输出
配置占位符,直到工作流与你的目标匹配,然后激活适合你用例的计划或 Webhook 路径。
通过将 HTTP 请求节点更改为与解决的令牌一起发送 POST 凭证,可以将抓取模板适应于登录流程。有关现成的登录工作流模板,请参阅专门的验证码类型指南(reCAPTCHA、Turnstile 等)。
那么你必须修改工作流。主示例中的 reCAPTCHA 设置和请求模式并不适用于所有情况。CapSolver 支持 Cloudflare Turnstile、Cloudflare Challenge、GeeTest V3/V4、DataDome、AWS WAF、MTCaptcha 等。更新 CapSolver 解决步骤和受保护请求,以匹配目标实际使用的挑战类型和令牌提交模式。
在目标网站期望的位置。这可能包括:
价格监控模板使用 header。其他网站可能不同。
通常需要。即使使用相同验证码家族的网站,也可能需要不同的 websiteURL、websiteKey、pageAction、不可见设置或其他任务选项。
可以。将选择器和输出逻辑替换为你需要的任何内容,例如库存、标题、SKU、描述、受保护内容、登录状态或网站健康信号。
首先检查 HTTP 请求节点返回的受保护响应。
按顺序调试工作流:解决 -> 提交 -> 检查响应 -> 提取。
学习可扩展的Rust网络爬虫架构,包括reqwest、scraper、异步爬取、无头浏览器爬取、代理轮换以及符合规范的验证码处理。
