产品集成资源文档定价
立即开始

© 2026 CapSolver. All rights reserved.

联系我们

Slack: lola@capsolver.com

产品

  • reCAPTCHA v2
  • reCAPTCHA v3
  • Cloudflare Turnstile
  • Cloudflare Challenge
  • AWS WAF
  • 浏览器插件
  • 更多验证码类型

集成

  • Selenium
  • Playwright
  • Puppeteer
  • n8n
  • 合作伙伴
  • 查看所有集成

资源

  • 推荐返佣系统
  • 官方文档
  • API 参考
  • 博客
  • 常见问题 (FAQ)
  • 术语表
  • 系统状态

法律声明

  • 服务条款
  • 隐私政策
  • 退款政策
  • 请勿出售我的信息
博客/All/如何在n8n中使用CapSolver构建网络爬虫
Apr03, 2026

如何在n8n中使用CapSolver构建网络爬虫

Ethan Collins

Ethan Collins

Pattern Recognition Specialist

如果你曾经尝试过抓取价格、产品数据或受保护页面内容,你就知道困难之处不仅在于加载URL。工作流还必须解决网站的验证码,以网站期望的方式提交解决后的令牌,然后从受保护的响应中提取正确的数据。

这就是为什么简单的“解决验证码即可”示例不足以实现真正的自动化。一个网站可能期望令牌出现在头部、表单正文、JSON负载、查询字符串、Cookie或其他特定于应用的字段中。它可能使用reCAPTCHA、Turnstile或其他完全不同的验证码挑战。一旦受保护的响应返回,选择器和输出逻辑仍需与你的目标相匹配。

在本指南中,你将学习如何使用CapSolver在n8n中构建受验证码保护的网站抓取器。主要的演示流程基于仓库工作流 Scraping — Price & Product Details — CapSolver + Schedule + Webhook,但相同的模板可以适应以下场景:

  • 抓取价格和产品数据
  • 检查库存或受保护内容的变化
  • 登录你的账户
  • 通过webhook从其他服务触发固定目标的抓取

本文内容是关于在你拥有、管理或被允许测试的目标上进行的实用、授权自动化。

重要提示: 这些工作流是示例和起点模板,不是通用的直接使用配方。你应预期需要修改验证码设置、令牌提交方式、请求负载、头部、Cookie、提取选择器和输出逻辑,以匹配每个特定网站。


本指南构建的内容

本文中的主要示例是一个固定目标抓取器模板,现在支持两种激活模式:

  • 定时触发: 每6小时自动运行
  • Webhook触发: 按需触发相同的配置目标

在默认的仓库模板中,该工作流:

  • 解决reCAPTCHA v3
  • 获取受保护的产品页面
  • 通过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中的工作流之前不会生效。


先决条件

在开始之前,请确保你已具备以下条件:

  1. 一个n8n实例
  2. 一个带有API密钥和余额的CapSolver账户
  3. 在n8n中配置好的CapSolver节点
  4. 一个目标URL和你想提取的字段
  5. 该目标所需的验证码参数
  6. 明确了解受保护请求在浏览器中是如何实际提交的

对于本文中的主要示例,假设目标使用reCAPTCHA,所以关键值为:

  • websiteURL
  • websiteKey
  • pageAction(用于reCAPTCHA v3)

在仓库的价格监控模板中,CapSolver节点配置为:

  • operation: reCAPTCHA v3
  • websiteURL: https://YOUR-TARGET-SITE.com/product-page
  • websiteKey: YOUR_SITE_KEY_HERE
  • pageAction: view_product

当你检查一个reCAPTCHA目标时,请验证:

  • 该目标使用的是哪个reCAPTCHA版本
  • 是否需要pageAction
  • 解决后的令牌实际提交到哪里

重要提示: 这不是通用的验证码检测部分。如果目标使用了其他挑战类型——如Cloudflare Turnstile、Cloudflare Challenge、GeeTest、DataDome、AWS WAF或MTCaptcha——你需要更改CapSolver设置和提交解决后令牌的HTTP请求。


主工作流:Scraping — Price & Product Details — CapSolver + Schedule + Webhook

仓库工作流 Scraping — Price & Product Details — CapSolver + Schedule + Webhook 现在支持两种固定目标激活路径:

  • Every 6 Hours 用于定期检查
  • Webhook Trigger 用于按需运行

两种路径都使用相同的占位符和相同的抓取逻辑。Webhook版本只是以**Respond to Webhook**结束,这样调用者可以以JSON形式获取最终的警报或无变化负载。

定时路径

定时路径使用以下节点:

  1. Every 6 Hours
  2. Solve reCAPTCHA v3
  3. Fetch Product Page
  4. Extract Data
  5. Compare Data
  6. Data Changed?
  7. Build Alert
  8. No Change

Webhook路径

Webhook路径为相同的固定目标重复相同的逻辑:

  1. Webhook Trigger
  2. Solve reCAPTCHA v3 [Webhook]
  3. Fetch Product Page [Webhook]
  4. Extract Data [Webhook]
  5. Compare Data [Webhook]
  6. Data Changed? [Webhook]
  7. Build Alert [Webhook]
  8. No Change [Webhook]
  9. Respond to Webhook

主要逻辑如何工作

1. 触发器

当需要定期检查时(如抓取或库存检查)使用定时触发器。

当其他工作流、服务或应用程序需要按需激活相同配置的目标时使用Webhook。

2. 解决reCAPTCHA v3

模板使用:

设置 值
操作 reCAPTCHA v3
websiteURL https://YOUR-TARGET-SITE.com/product-page
websiteKey YOUR_SITE_KEY_HERE
pageAction view_product

3. 获取受保护页面

解决后的令牌通过请求头部提交:

头部 值
user-agent 浏览器风格的用户代理
x-recaptcha-token {{ $json.data.solution.gRecaptchaResponse }}

这是工作流中最重要的细节之一。仓库模板不假设令牌始终在g-recaptcha-response中。在这个示例中,它被放在自定义头部中。

4. 提取price和productName

HTML节点提取:

键 CSS选择器
price .product-price, [data-price], .price
productName h1, .product-title

5. 与之前运行进行比较

代码节点使用以下内容存储并比较值:

  • $workflow.staticData.lastPrice
  • $workflow.staticData.lastChecked

这使得工作流能够区分:

  • 首次检查
  • 无变化
  • 价格上涨
  • 价格下降

6. 分支到警报或无变化

IF节点检查 {{ $json.changed }}。

如果价格变化,工作流进入 Build Alert。

如果没有变化,它进入 No Change。

在Webhook路径中,任一分支随后进入 Respond to Webhook。


为什么这实际上是一个抓取器模板

尽管主要示例包含价格比较逻辑,但更实用的思考方式是将其视为一个带有比较逻辑的受保护产品页面抓取器模板。

可重用的部分包括:

  • 触发模式
  • 验证码类型
  • 解决设置
  • 受保护请求
  • 提取选择器
  • 比较或输出逻辑

这就是为什么相同的结构可以驱动:

  • 抓取
  • 产品详情提取
  • 库存检查
  • 受保护页面验证
  • 你自己的账户登录检查

你可能需要更改的内容

这是在真实目标上最重要的部分。

对于大多数真实网站,你应该假设这里几乎一切都可调整:挑战类型、解决参数、令牌提交位置、请求体、Cookie、头部、选择器,甚至最终的成功标准。这些仓库工作流是可工作的模式示例,而不是适用于每个网站的固定配方。

1. 触发模式

仓库的使用场景工作流现在支持定时 + Webhook。

使用:

  • 定时用于定期检查
  • Webhook用于从其他服务按需激活

这些模板中的Webhook路径是固定目标激活器,而不是用于任意用户提供的目标的公共API。

2. 验证码类型

主要的价格监控示例使用reCAPTCHA v3,但你的目标可能使用:

  • reCAPTCHA v2
  • reCAPTCHA v3
  • Cloudflare Turnstile
  • Cloudflare Challenge
  • GeeTest V3 / V4
  • DataDome
  • AWS WAF
  • MTCaptcha
  • 其他支持的挑战类型

如果类型改变,你需要相应地更新解决步骤。

3. 验证码解决设置

即使两个网站都使用reCAPTCHA,设置也可能不同。

你可能需要更改:

  • CapSolver的operation或任务类型
  • websiteURL
  • websiteKey
  • pageAction
  • 不可见/企业相关选项
  • 任何其他特定于网站的挑战设置

抓取器模板将这些作为占位符配置字段暴露出来,因为它们预计会变化。

4. 令牌提交的位置

不要假设令牌总是提交到相同的位置。

该仓库已经展示了几种模式:

模式 仓库示例
请求头部 Scraping — CapSolver + Schedule 使用x-recaptcha-token
表单正文 登录流程通常在表单编码的正文使用g-recaptcha-response

在真实网站上,解决后的令牌可能位于:

  • 头部
  • 表单字段
  • JSON负载
  • 查询参数
  • Cookie
  • 隐藏字段
  • 其他应用特定的值

此仓库示例是一种提交模式,而不是通用模式。

5. 受保护请求本身

受保护请求可能需要的不仅仅是验证码或挑战令牌。

你可能需要调整:

  • 头部
  • Cookie
  • CSRF值
  • 隐藏字段
  • 表单编码
  • JSON正文结构
  • 请求方法

这在以下场景中尤其常见:

  • 登录流程
  • 结账或受限制表单提交

6. 你提取的内容

主要抓取器模板提取:

  • price
  • productName

但你可以替换为:

  • 标题
  • 库存状态
  • SKU
  • 描述
  • 变体值
  • 可用性文本
  • 完整的受保护内容块

7. 你比较或输出的内容

当前的价格监控代码比较数值,但相同模式可以调整为:

  • 价格变化
  • 库存变化
  • 登录成功
  • 注册成功
  • 内容差异
  • 网站健康状态
  • 无需比较的原始数据导出

导入此工作流

以下JSON是此仓库中**Scraping — Price & Product Details — CapSolver + Schedule + Webhook的当前可导入版本,包括定时 + Webhook**激活路径。

点击展开工作流JSON
json Copy
{
  "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 路径:

bash Copy
curl -X POST https://your-n8n-instance.com/webhook/price-monitor \
  -H "Content-Type: application/json" \
  -d '{}'

预期响应(首次检查):

json Copy
{
  "status": "no_change",
  "currentPrice": "$29.99",
  "checkedAt": "2026-03-11T08:00:00.000Z"
}

预期响应(价格变动):

json Copy
{
  "alert": "Widget Pro 价格下降:$39.99 → $29.99(-$10.00)",
  "severity": "deal",
  "checkedAt": "2026-03-11T14:00:00.000Z"
}

包含实际价格数据的响应表明整个流程已正常运行——验证码已解决、受保护页面已获取、数据已提取且比较逻辑已执行。


故障排除

已解决令牌,但仍被阻止

如果 CapSolver 返回了令牌但网站仍然阻止请求,问题通常不在于验证码的解决本身。常见原因包括:

  • 验证码类型错误
  • 解决设置不正确
  • pageAction 错误
  • 令牌提交位置错误
  • 缺少 cookies、headers 或隐藏字段

网站使用了不同的挑战类型

如果目标网站使用的是非 reCAPTCHA 挑战(如 Cloudflare Turnstile、Cloudflare Challenge、GeeTest、DataDome、AWS WAF 或 MTCaptcha),则主示例无法直接使用。你需要更新:

  • CapSolver 节点配置
  • 预期的挑战参数
  • 提交令牌的受保护请求

选择器返回空值

如果 HTML 节点未能提取你期望的字段:

  • 检查是否确实收到了受保护页面
  • 根据返回的 HTML 确认选择器
  • 确保数据存在于原始响应中,而不是仅在客户端渲染后出现

Webhook 无法工作

仓库模板导入时处于非活动状态。直到你:

  1. 配置占位符
  2. 选择凭证
  3. 激活工作流

Webhook 路径将不会处于活动状态。


最佳实践

  1. 在解决令牌后立即使用它。
  2. 在将其复制到 n8n 之前,检查精确的浏览器请求。
  3. 假设每个目标可能需要不同的解决设置。
  4. 假设每个目标可能需要不同的提交逻辑。
  5. 在调试选择器之前验证受保护的响应。
  6. 将仓库模板视为起点,而不是通用的直接插入方案。
  7. 除非你有理由暴露更多目标,否则保持 Webhook 模板为固定目标。
  8. 测试完整的解决 -> 提交 -> 提取流程,而不仅仅是解决步骤。
  9. 在 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
  • 一个表单 body
  • 一个 JSON 负载
  • 一个查询参数
  • 一个 cookie
  • 一个隐藏字段

价格监控模板使用 header。其他网站可能不同。

我需要为每个网站更改 CapSolver 设置吗?

通常需要。即使使用相同验证码家族的网站,也可能需要不同的 websiteURL、websiteKey、pageAction、不可见设置或其他任务选项。

我可以抓取价格以外的内容吗?

可以。将选择器和输出逻辑替换为你需要的任何内容,例如库存、标题、SKU、描述、受保护内容、登录状态或网站健康信号。

我如何确定请求负载或选择器是否错误?

首先检查 HTTP 请求节点返回的受保护响应。

  • 如果响应仍然被阻止或不完整,请求格式可能有误。
  • 如果响应正确但提取失败,选择器或解析逻辑可能有误。

按顺序调试工作流:解决 -> 提交 -> 检查响应 -> 提取。

查看更多

n8nMar 09, 2026

如何使用 CapSolver 和 n8n 解决 reCAPTCHA v2/v3

使用 CapSolver 和 n8n 构建一个 eCAPTCHA v2/v3 解决方案 API。学习如何自动化获取 token、提交到网站,并在无需编码的情况下提取受保护的数据。

Ethan Collins
Ethan Collins
Apr 22, 2026

解决图像谜题的最佳AI:2026顶级工具和策略

发现解决图像谜题的最佳AI。了解CapSolver的视觉引擎和ImageToText API如何以高精度自动化处理复杂的视觉挑战。

Ethan Collins
Ethan Collins

目录

web scrapingApr 22, 2026

Rust网络爬虫架构:可扩展的数据提取

学习可扩展的Rust网络爬虫架构,包括reqwest、scraper、异步爬取、无头浏览器爬取、代理轮换以及符合规范的验证码处理。

Ethan Collins
Ethan Collins
Apr 22, 2026

搜索API与知识供应链:人工智能数据基础设施指南

了解搜索API工具、知识供应链、SERP API工作流程和AI数据管道如何塑造现代人工智能网络数据基础设施。

Ethan Collins
Ethan Collins