8
n8n 中文网amn8n.com

多站点产品价格监控:Claude-Sonnet、Google Sheets和Telegram提醒

高级

这是一个Market Research, AI Summarization领域的自动化工作流,包含 19 个节点。主要使用 Code, Wait, Merge, SplitOut, Telegram 等节点。 使用Firecrawl、Claude-Sonnet AI和Telegram提醒的电商价格监控系统

前置要求
  • Telegram Bot Token
  • 可能需要目标 API 的认证凭证
  • Google Sheets API 凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "YnD0kvM9AYMjYxmj",
  "meta": {
    "instanceId": "b91e510ebae4127f953fd2f5f8d40d58ca1e71c746d4500c12ae86aad04c1502",
    "templateCredsSetupCompleted": true
  },
  "name": "多站点产品价格监控:Claude-Sonnet、Google Sheets和Telegram提醒",
  "tags": [
    {
      "id": "7zsdOA50QGm7RNqx",
      "name": "Monitoring",
      "createdAt": "2025-10-23T16:41:17.031Z",
      "updatedAt": "2025-10-23T16:41:17.031Z"
    },
    {
      "id": "BL8TsHYj5FkNYzfi",
      "name": "E-commerce",
      "createdAt": "2025-10-23T16:41:16.985Z",
      "updatedAt": "2025-10-23T16:41:16.985Z"
    },
    {
      "id": "dlf9zFSN3j6s2jgO",
      "name": "Business Intelligence",
      "createdAt": "2025-10-23T16:41:17.008Z",
      "updatedAt": "2025-10-23T16:41:17.008Z"
    },
    {
      "id": "lpozR2Ct8reF9bCk",
      "name": "AI",
      "createdAt": "2025-10-23T16:41:17.062Z",
      "updatedAt": "2025-10-23T16:41:17.062Z"
    }
  ],
  "nodes": [
    {
      "id": "306294b3-27b8-4821-9b04-0e8d210af07e",
      "name": "⏰ 每6小时监控一次",
      "type": "n8n-nodes-base.scheduleTrigger",
      "notes": "Triggers the workflow every 6 hours to check competitor data",
      "position": [
        -832,
        352
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "8b934690-1e26-4810-820d-8ff785691adf",
      "name": "抓取URL并获取其内容",
      "type": "@mendable/n8n-nodes-firecrawl.firecrawl",
      "position": [
        -1408,
        96
      ],
      "parameters": {
        "url": "https://www.on.com/en-sg/shop/mens/shoes",
        "operation": "scrape",
        "scrapeOptions": {
          "options": {
            "formats": {
              "format": [
                {
                  "type": "json",
                  "prompt": "price of the shoe"
                }
              ]
            },
            "headers": {}
          }
        },
        "requestOptions": {}
      },
      "credentials": {
        "firecrawlApi": {
          "id": "t2Tp5wdqfarSfsZ4",
          "name": "Firecrawl account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "90ed5eb7-6236-47e1-88c6-c5278efe24fa",
      "name": "向模型发送消息",
      "type": "n8n-nodes-base.perplexity",
      "position": [
        -1568,
        0
      ],
      "parameters": {
        "model": "sonar",
        "options": {},
        "messages": {
          "message": [
            {
              "content": "Price of the shoe  "
            }
          ]
        },
        "requestOptions": {}
      },
      "credentials": {
        "perplexityApi": {
          "id": "hmDBlVXt4GJKfIfz",
          "name": "Perplexity account new"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e1e006de-d363-4e5a-adbd-77ffc901f42d",
      "name": "拆分输出",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -1296,
        0
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "citations"
      },
      "typeVersion": 1
    },
    {
      "id": "11fdf8bc-aa37-4f42-8bbe-8e16c47c8b47",
      "name": "获取Apify结果",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1216,
        272
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/compass~google-maps-extractor/runs/last/dataset/items?token=YOUR API KEY",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "vPMKFhRN7iuRAL86",
          "name": "Apify"
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "b6a20e25-d82a-493b-b305-066e043f6ba1",
      "name": "启动Apify执行器",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1616,
        272
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/compass~google-maps-extractor/runs?token=YOUR API KEY",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n    \"language\": \"en\",\n    \"locationQuery\": \"worldwide\",\n    \"maxCrawledPlacesPerSearch\": 50,\n    \"searchStringsArray\": [\n        \"shoe stores\",\n        \"shoe shops\"\n    ],\n    \"skipClosedPlaces\": false\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "vPMKFhRN7iuRAL86",
          "name": "Apify"
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "47a1e71a-6bbf-4cfe-82f1-b46744d247ee",
      "name": "将非结构化的AI文本转换为有组织、可用的数据字段",
      "type": "n8n-nodes-base.code",
      "notes": "Parses and validates the AI extracted data",
      "position": [
        -576,
        192
      ],
      "parameters": {
        "jsCode": "// Parse AI response and clean data\nconst items = [];\n\nfor (const item of $input.all()) {\n  try {\n    // Parse the AI response\n    let parsed;\n    const response = item.json.choices?.[0]?.message?.content || item.json.message || '';\n    \n    // Remove markdown code blocks if present\n    const cleaned = response.replace(/```json\\n?|```\\n?/g, '').trim();\n    \n    try {\n      parsed = JSON.parse(cleaned);\n    } catch (e) {\n      // Try to extract JSON from the response\n      const jsonMatch = cleaned.match(/\\{[\\s\\S]*\\}/);\n      if (jsonMatch) {\n        parsed = JSON.parse(jsonMatch[0]);\n      } else {\n        throw new Error('Could not parse JSON from AI response');\n      }\n    }\n    \n    // Enrich with metadata\n    items.push({\n      json: {\n        ...parsed,\n        scrapedAt: new Date().toISOString(),\n        priceChange: 0, // Will be calculated in comparison\n        priceChangePercent: 0,\n        isNewLow: false,\n        alertLevel: 'none'\n      }\n    });\n  } catch (error) {\n    console.error('Failed to parse item:', error.message);\n    // Add error item for debugging\n    items.push({\n      json: {\n        error: error.message,\n        rawResponse: item.json,\n        competitorName: 'Parse Error',\n        scrapedAt: new Date().toISOString()\n      }\n    });\n  }\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "3e8d7348-ccef-4cb5-aca4-d5ed6f210199",
      "name": "🔀 当前数据与历史数据合并1",
      "type": "n8n-nodes-base.merge",
      "notes": "Combines current scrape with historical data for comparison",
      "position": [
        -384,
        224
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "fieldsToMatchString": "rawResponse.message.content"
      },
      "typeVersion": 3
    },
    {
      "id": "7e24cbab-31e4-47ba-97f2-9a7a71335905",
      "name": "🔍 检测价格和库存变化1",
      "type": "n8n-nodes-base.code",
      "notes": "Intelligent change detection with alert level classification",
      "position": [
        -208,
        224
      ],
      "parameters": {
        "jsCode": "// Compare current prices with historical and detect changes\nconst results = [];\n\nfor (const item of $input.all()) {\n  const current = item.json;\n  \n  // Skip error items\n  if (current.error) {\n    results.push({ json: current });\n    continue;\n  }\n  \n  // Find historical data for this competitor\n  const historical = $('📊 Read Historical Data').all()\n    .find(h => h.json.competitorName === current.competitorName);\n  \n  let alertLevel = 'none';\n  let changes = [];\n  \n  if (historical && historical.json.currentPrice) {\n    const oldPrice = parseFloat(historical.json.currentPrice);\n    const newPrice = parseFloat(current.currentPrice);\n    const priceChange = newPrice - oldPrice;\n    const priceChangePercent = ((priceChange / oldPrice) * 100).toFixed(2);\n    \n    current.priceChange = priceChange;\n    current.priceChangePercent = parseFloat(priceChangePercent);\n    current.previousPrice = oldPrice;\n    \n    // Determine alert level based on price changes\n    if (Math.abs(priceChangePercent) >= 20) {\n      alertLevel = 'critical';\n      changes.push(`Price ${priceChange > 0 ? 'increased' : 'decreased'} by ${Math.abs(priceChangePercent)}%`);\n    } else if (Math.abs(priceChangePercent) >= 10) {\n      alertLevel = 'warning';\n      changes.push(`Price ${priceChange > 0 ? 'increased' : 'decreased'} by ${Math.abs(priceChangePercent)}%`);\n    } else if (Math.abs(priceChangePercent) >= 5) {\n      alertLevel = 'info';\n      changes.push(`Minor price change: ${priceChangePercent}%`);\n    }\n    \n    // Check if it's a new low price\n    const historicalLow = parseFloat(historical.json.lowestPrice || oldPrice);\n    if (newPrice < historicalLow) {\n      current.isNewLow = true;\n      alertLevel = alertLevel === 'none' ? 'info' : alertLevel;\n      changes.push('🎯 NEW LOWEST PRICE!');\n    }\n    current.lowestPrice = Math.min(newPrice, historicalLow);\n    \n    // Stock level changes\n    if (historical.json.stockLevel !== current.stockLevel) {\n      if (current.stockLevel === 'Out of Stock') {\n        alertLevel = 'critical';\n        changes.push('📦 Product went OUT OF STOCK');\n      } else if (current.stockLevel === 'Low Stock') {\n        alertLevel = alertLevel === 'none' ? 'warning' : alertLevel;\n        changes.push('⚠️ Stock level is LOW');\n      } else if (historical.json.stockLevel === 'Out of Stock' && current.inStock) {\n        alertLevel = alertLevel === 'none' ? 'info' : alertLevel;\n        changes.push('✅ Back in stock!');\n      }\n    }\n    \n    // Rating changes\n    const oldRating = parseFloat(historical.json.rating || 0);\n    const newRating = parseFloat(current.rating || 0);\n    const ratingChange = newRating - oldRating;\n    \n    if (Math.abs(ratingChange) >= 0.5) {\n      alertLevel = alertLevel === 'none' ? 'info' : alertLevel;\n      changes.push(`⭐ Rating ${ratingChange > 0 ? 'improved' : 'dropped'} by ${Math.abs(ratingChange).toFixed(1)} stars`);\n    }\n    \n    // Review count changes\n    const oldReviews = parseInt(historical.json.reviewCount || 0);\n    const newReviews = parseInt(current.reviewCount || 0);\n    const reviewDiff = newReviews - oldReviews;\n    \n    if (reviewDiff > 0) {\n      changes.push(`💬 ${reviewDiff} new review${reviewDiff > 1 ? 's' : ''}`);\n    }\n  } else {\n    // First time seeing this competitor\n    alertLevel = 'info';\n    changes.push('🆕 First time tracking this competitor');\n    current.lowestPrice = current.currentPrice;\n  }\n  \n  current.alertLevel = alertLevel;\n  current.changesSummary = changes.join(' | ');\n  current.hasChanges = alertLevel !== 'none';\n  \n  results.push({ json: current });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "4b8b4578-e6aa-461a-ae0a-7289bd0bf7e3",
      "name": "💾 更新历史数据1",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Saves current data to historical tracking sheet",
      "position": [
        -32,
        320
      ],
      "parameters": {
        "columns": {
          "value": {
            "rating": "={{ $json.rating }}",
            "inStock": "={{ $json.inStock }}",
            "currency": "={{ $json.currency }}",
            "scrapedAt": "={{ $json.scrapedAt }}",
            "productUrl": "={{ $json.productUrl }}",
            "stockLevel": "={{ $json.stockLevel }}",
            "lowestPrice": "={{ $json.lowestPrice }}",
            "productName": "={{ $json.productName }}",
            "reviewCount": "={{ $json.reviewCount }}",
            "currentPrice": "={{ $json.currentPrice }}",
            "originalPrice": "={{ $json.originalPrice }}",
            "competitorName": "={{ $json.competitorName }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "mode": "name",
          "value": "Historical Data"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "= {{ $env.GOOGLE_SHEET_ID }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "aGWAWeimeVnptYQh",
          "name": "Google Sheets account 2"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "28d2de55-ef62-40cd-a22b-a13eee3c2dc8",
      "name": "当点击\"执行工作流\"时",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1760,
        144
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "62cf73d2-a328-463b-9ea5-0d3fa8e3ba56",
      "name": "🤖 使用Claude-Sonnet 4.5进行AI产品数据提取",
      "type": "@n8n/n8n-nodes-langchain.anthropic",
      "position": [
        -864,
        192
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-5-20250929",
          "cachedResultName": "claude-sonnet-4-5-20250929"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "You are an accurate e-commerce data parser. From the provided HTML, extract shoe details and output a pure JSON object—no markdown or extra text.\n\nInclude the following fields:\n\n- productName: string \n- currentPrice: number (numeric value only, no currency symbols) \n- originalPrice: number (if discounted, otherwise same as currentPrice) \n- currency: string (USD, EUR, etc.) \n- inStock: boolean \n- stockLevel: string (\"In Stock\", \"Low Stock\", \"Out of Stock\", \"Limited\", etc.) \n- rating: number (0-5 scale) \n- reviewCount: number \n- lastUpdated: string (current ISO timestamp) \n- productUrl: string (from context) \n- competitorName: string (from context)\n\nOutput only the JSON object—no notes, comments, or formatting.\n"
            }
          ]
        }
      },
      "credentials": {
        "anthropicApi": {
          "id": "S8laStQPC1u3EYuZ",
          "name": "Anthropic account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e5998861-2d29-4fdb-a5c0-445cf79ac50e",
      "name": "🔀 当前数据与历史数据合并",
      "type": "n8n-nodes-base.merge",
      "notes": "Combines current scrape with historical data for comparison",
      "position": [
        -1024,
        176
      ],
      "parameters": {
        "numberInputs": 3
      },
      "typeVersion": 3
    },
    {
      "id": "6815d6c1-43c5-49a8-8f41-eafa0ee83363",
      "name": "📊 读取历史数据",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Loads previous scan data for comparison",
      "position": [
        -624,
        352
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "mode": "name",
          "value": "Historical Data"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.GOOGLE_SHEET_ID }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "aGWAWeimeVnptYQh",
          "name": "Google Sheets account 2"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "0c300e39-74da-4947-ab75-1360158f48a1",
      "name": "📝 记录提醒详情",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Logs all alerts to separate tracking sheet",
      "position": [
        -48,
        160
      ],
      "parameters": {
        "columns": {
          "value": {
            "rating": "={{ $json.rating }}",
            "timestamp": "={{ $json.scrapedAt }}",
            "alertLevel": "={{ $json.alertLevel }}",
            "productUrl": "={{ $json.productUrl }}",
            "stockLevel": "={{ $json.stockLevel }}",
            "priceChange": "={{ $json.priceChange || 0 }}",
            "productName": "={{ $json.productName }}",
            "currentPrice": "={{ $json.currentPrice }}",
            "previousPrice": "={{ $json.previousPrice || 'N/A' }}",
            "changesSummary": "={{ $json.changesSummary }}",
            "competitorName": "={{ $json.competitorName }}",
            "priceChangePercent": "={{ $json.priceChangePercent || 0 }}"
          },
          "mappingMode": "defineBelow"
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "mode": "name",
          "value": "Alert Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.GOOGLE_SHEET_ID }}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "aGWAWeimeVnptYQh",
          "name": "Google Sheets account 2"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "1833ee8f-13ca-40f3-a3ce-e7c09bbff5fc",
      "name": "发送短信",
      "type": "n8n-nodes-base.telegram",
      "position": [
        112,
        240
      ],
      "webhookId": "ae976d33-11d1-4d92-9289-fc86a1525828",
      "parameters": {
        "text": "The competitors’ pricing has been finalized.",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "id": "mYO3O1232v1KxESH",
          "name": "Telegram account 2"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1209134e-cb58-4525-bbcb-ffee2a890ddb",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2336,
        -336
      ],
      "parameters": {
        "width": 2576,
        "height": 880,
        "content": "## 简介"
      },
      "typeVersion": 1
    },
    {
      "id": "864886e9-21d0-402f-8f1f-46216a5e1373",
      "name": "暂停工作流以等待Apify抓取任务完成",
      "type": "n8n-nodes-base.wait",
      "position": [
        -1424,
        272
      ],
      "webhookId": "426f22d5-31db-4344-ae7a-13dedfc46bf4",
      "parameters": {
        "amount": 25
      },
      "typeVersion": 1.1
    },
    {
      "id": "5b798a21-b3e1-4fb4-816a-d32a63367933",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -608,
        -304
      ],
      "parameters": {
        "color": 5,
        "width": 352,
        "height": 256,
        "content": "## Google Sheets结构"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "7e9182a3-7a64-45a2-9632-3de5d2917fcd",
  "connections": {
    "Split Out": {
      "main": [
        [
          {
            "node": "🔀 Merge Current with Historical",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Message a model": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Results Apify": {
      "main": [
        [
          {
            "node": "🔀 Merge Current with Historical",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Start Actor Apify": {
      "main": [
        [
          {
            "node": "Pause the workflow to let the Apify scraping task finish",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📝 Log Alert Details": {
      "main": [
        [
          {
            "node": "Send a text message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "⏰ Monitor Every 6 Hours": {
      "main": [
        [
          {
            "node": "📊 Read Historical Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📊 Read Historical Data": {
      "main": [
        [
          {
            "node": "🔀 Merge Current with Historical1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "💾 Update Historical Data1": {
      "main": [
        [
          {
            "node": "Send a text message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape a url and get its content": {
      "main": [
        [
          {
            "node": "🔀 Merge Current with Historical",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "🔀 Merge Current with Historical": {
      "main": [
        [
          {
            "node": "🤖 AI Extract Product Data using Claude-Sonnet 4.5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔍 Detect Price & Stock Changes1": {
      "main": [
        [
          {
            "node": "💾 Update Historical Data1",
            "type": "main",
            "index": 0
          },
          {
            "node": "📝 Log Alert Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔀 Merge Current with Historical1": {
      "main": [
        [
          {
            "node": "🔍 Detect Price & Stock Changes1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "Message a model",
            "type": "main",
            "index": 0
          },
          {
            "node": "Scrape a url and get its content",
            "type": "main",
            "index": 0
          },
          {
            "node": "Start Actor Apify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🤖 AI Extract Product Data using Claude-Sonnet 4.5": {
      "main": [
        [
          {
            "node": "Converts unstructured AI text into organized, usable data fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pause the workflow to let the Apify scraping task finish": {
      "main": [
        [
          {
            "node": "Get Results Apify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Converts unstructured AI text into organized, usable data fields": {
      "main": [
        [
          {
            "node": "🔀 Merge Current with Historical1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 市场调研, AI 摘要总结

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量19
分类2
节点类型13
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Cheng Siong Chin

Cheng Siong Chin

@cschin

Prof. Cheng Siong CHIN serves as Chair Professor in Intelligent Systems Modelling and Simulation in Newcastle University, Singapore. His academic credentials include an M.Sc. in Advanced Control and Systems Engineering from The University of Manchester and a Ph.D. in Robotics from Nanyang Technological University.

外部链接
在 n8n.io 查看

分享此工作流