多站点产品价格监控: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 凭证
使用的节点 (19)
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 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)可能需要您自行付费。
相关工作流推荐
使用Firecrawl、GPT-4.1监控竞争对手价格并发送警报到Gmail
使用Firecrawl、GPT-4.1、Sheets和Gmail警报监控竞争对手价格
Code
Gmail
Merge
+5
15 节点Cheng Siong Chin
市场调研
新抓取器_TechCrunch新闻-AI1
TechCrunch AI文章抓取与分类器,使用GPT-4.1-nano到Sheets和Telegram
Set
Code
Html
+12
18 节点Mujahid Kabae
市场调研
潜在客户开发与邮件工作流
使用Google Maps、SendGrid和AI自动化B2B潜在客户开发与邮件营销
If
Set
Code
+21
141 节点Ezema Kingsley Chibuzo
潜在客户开发
01 使用AI媒体买家分析Facebook广告表现并将洞察发送到Google Sheets
使用Gemini AI分析Facebook广告并将洞察发送到Google Sheets
If
Set
Code
+13
34 节点JJ Tham
市场调研
社区问题监控器与OpenRouter AI、Reddit和论坛爬取
使用OpenRouter AI、Reddit和论坛爬取监控社区问题
Set
Code
Html
+13
29 节点Julian Kaiser
市场调研
源发现 - 自动搜索更及时的信息源
多平台源发现系统,集成 SerpAPI、DuckDuckGo、GitHub、Reddit 和 Bluesky
Set
Code
Limit
+18
68 节点Hybroht
市场调研
工作流信息
难度等级
高级
节点数量19
分类2
节点类型13
作者
Cheng Siong Chin
@cschinProf. 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 查看 →
分享此工作流