CoinGecko加密货币价格预测管道:Gemini AI、Decodo和Gmail
高级
这是一个Crypto Trading领域的自动化工作流,包含 24 个节点。主要使用 Set, Code, Gmail, DataTable, SplitInBatches 等节点。 CoinGecko加密货币价格预测管道:Gemini AI、Decodo和Gmail
前置要求
- •Google 账号和 Gmail API 凭证
- •Google Gemini API Key
使用的节点 (24)
分类
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"meta": {
"instanceId": "689fa22e68cd4198e4ae37f3cc44f498087edd235a867e22515be823bab694c7",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "0c833f46-cfe8-4a41-8c4d-a6cb16492b7d",
"name": "**关键:** 无需 API - 直接 HTML 抓取",
"type": "n8n-nodes-base.code",
"position": [
1984,
2128
],
"parameters": {
"jsCode": "const data = items[0].json.output;\n\nlet cleaned = data.trim();\n\nif (cleaned.startsWith(\"```\")) {\n cleaned = cleaned\n .replace(/^```(json)?/i, \"\")\n .replace(/```$/, \"\")\n .trim();\n}\n\nlet parsed;\ntry {\n parsed = JSON.parse(cleaned);\n} catch (e) {\n throw new Error(\"❌ Cannot parse JSON from AI Agent: \" + e.message + \"\\nData: \" + cleaned);\n}\n\nreturn [\n {\n json: parsed\n }\n];\n"
},
"typeVersion": 2
},
{
"id": "7adcc446-89d8-45d3-8e38-976c6c540e83",
"name": "**注意:** 将搜索 URL 替换为您的目标位置和业务类型",
"type": "n8n-nodes-base.dataTable",
"position": [
1408,
1808
],
"parameters": {
"columns": {
"value": {
"ts": "={{ $json.ts }}",
"coin": "={{ $json.coin }}",
"price": "={{ $json.price }}",
"change_1h": "={{ $json.change_1h }}",
"change_7d": "={{ $json.change_7d }}",
"change_24h": "={{ $json.change_24h }}",
"market_cap": "={{ $json.market_cap }}",
"volume_24h": "={{ $json.volume_24h }}"
},
"schema": [
{
"id": "coin",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "coin",
"defaultMatch": false
},
{
"id": "ts",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "ts",
"defaultMatch": false
},
{
"id": "price",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "price",
"defaultMatch": false
},
{
"id": "change_1h",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "change_1h",
"defaultMatch": false
},
{
"id": "change_24h",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "change_24h",
"defaultMatch": false
},
{
"id": "change_7d",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "change_7d",
"defaultMatch": false
},
{
"id": "market_cap",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "market_cap",
"defaultMatch": false
},
{
"id": "volume_24h",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "volume_24h",
"defaultMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "b3ek8fMoxaftXS9A",
"cachedResultUrl": "/projects/I8J05gT6Zk37Q2dE/datatables/b3ek8fMoxaftXS9A",
"cachedResultName": "Coin Prices"
}
},
"typeVersion": 1
},
{
"id": "6d65fa35-bf5d-4b92-b4cd-942987fe2e3a",
"name": "多表:您可以连接多个表以实现有组织的数据结构",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
1520,
2928
],
"parameters": {
"jsonSchemaExample": "{\n \"coin\": \"bitcoin\",\n \"latest\": {\n \"ts\": \"2025-10-20T01:02:27Z\",\n \"price\": 108177,\n \"change_1h\": 0.4,\n \"change_24h\": 1.2\n },\n \"direction_6h\": \"up\",\n \"direction_12h\": \"up\",\n \"direction_24h\": \"up\",\n \"best_up_window\": { \"start_iso\": \"2025-10-20T04:00:00Z\", \"end_iso\": \"2025-10-20T08:00:00Z\" },\n \"best_down_window\": null,\n \"confidence\": \"medium\"\n}"
},
"typeVersion": 1.3
},
{
"id": "e1570d01-cdf8-408f-ad5b-3991486b7670",
"name": "## 🔗 步骤 2: 网站 URL 处理",
"type": "n8n-nodes-base.code",
"position": [
1168,
2704
],
"parameters": {
"jsCode": "const rows = items.map(i => i.json);\n\n// keep last 48h only\nconst cutoff = Date.now() - 48 * 60 * 60 * 1000;\nconst recent = rows.filter(r => new Date(r.ts).getTime() >= cutoff);\n\n// group by coin\nconst byCoin = {};\nfor (const r of recent) {\n const c = String(r.coin).toLowerCase();\n if (!byCoin[c]) byCoin[c] = [];\n byCoin[c].push(r);\n}\n\n// downsample: 30m buckets, max 120 points\nfunction downsample30m(sortedArr) {\n const out = [];\n let lastBucket = -1;\n for (const r of sortedArr) {\n const t = new Date(r.ts).getTime();\n const bucket = Math.floor(t / (30 * 60 * 1000));\n if (bucket !== lastBucket) {\n out.push([new Date(t).toISOString(), Number(r.price_usd ?? r.price)]);\n lastBucket = bucket;\n }\n }\n return out.slice(-120);\n}\n\nconst out = [];\nfor (const [coin, arr] of Object.entries(byCoin)) {\n arr.sort((a,b)=> new Date(a.ts) - new Date(b.ts));\n if (!arr.length) continue;\n const last = arr[arr.length - 1];\n\n out.push({\n json: {\n coin,\n latest: {\n ts: last.ts,\n price: Number(last.price_usd ?? last.price),\n change_1h: typeof last.change_1h === 'number' ? last.change_1h : null,\n change_24h: typeof last.change_24h === 'number' ? last.change_24h : null\n },\n series: downsample30m(arr)\n }\n });\n}\n\nreturn out;"
},
"typeVersion": 2
},
{
"id": "9ad8435e-08ff-46bf-91bd-7f4026a065f2",
"name": "提取并清理商家网站 URL:",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
416,
2000
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 30
}
]
}
},
"typeVersion": 1.2
},
{
"id": "eaab942c-c6fa-4a6a-871e-83c0dcc6af6a",
"name": "1. **提取 URL:** JavaScript 正则表达式在 Google Maps 数据中查找所有网站 URL",
"type": "n8n-nodes-base.set",
"position": [
736,
2000
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cf2fee0b-6433-4fad-98e7-91ac8a46684e",
"name": "coin 1",
"type": "string",
"value": "bitcoin"
},
{
"id": "e2b7bd0c-4b93-4b18-b17a-e03eac7611b0",
"name": "coin 2",
"type": "string",
"value": "ethereum"
},
{
"id": "f3099a1f-351f-433e-be96-dc89c56eecb8",
"name": "coin 3",
"type": "string",
"value": "solana"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "08ea4ffc-e47b-4c62-83d3-26028aa2850e",
"name": "2. **过滤 Google URL:** 移除不相关的域名(google.com、gstatic 等)",
"type": "n8n-nodes-base.code",
"position": [
960,
2000
],
"parameters": {
"jsCode": "const input = items[0].json;\nconst out = [];\n\nfor (const [key, val] of Object.entries(input)) {\n if (key.toLowerCase().includes('coin') && val) {\n out.push({ json: { coin: val } });\n }\n}\n\nreturn out;"
},
"typeVersion": 2
},
{
"id": "4048d49b-3270-499c-a348-a5ce7f948908",
"name": "3. **去重:** 消除重复网站",
"type": "n8n-nodes-base.splitInBatches",
"position": [
1184,
2000
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "4212ffb5-e4e2-451b-8399-2e90c81ad684",
"name": "4. **限制:** 控制测试批次大小(生产环境请调整)",
"type": "@decodo/n8n-nodes-decodo.decodo",
"position": [
1408,
2000
],
"parameters": {
"geo": "=United States",
"url": "=https://www.coingecko.com/en/coins/{{ $json.coin }}"
},
"credentials": {
"decodoApi": {
"id": "Xc6AkeDSHbyutWEs",
"name": "Decodo Credentials Hendra"
}
},
"typeVersion": 1
},
{
"id": "4cf00c51-1630-44d1-83ba-6585fbc8ca28",
"name": "**结果:** 干净的商家网站列表,准备用于电子邮件提取",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1632,
2000
],
"parameters": {
"text": "=Extract coin data from the following website content and return it in the specified JSON format.\n\nWebsite content:\n{{ $json.data.results[0].content }}\n\nCRITICAL INSTRUCTIONS FOR PERCENTAGE CHANGES:\n\nThe content contains a table like this:\n| 1h | 24h | 7d | 14d | 30d | 1y |\n| --- | --- | --- | --- | --- | --- |\n| 0.5% | 5.1% | 1.2% | 9.7% | 8.9% | 54.3% |\n\nThese percentages DO NOT include +/- signs. You MUST:\n1. Search the ENTIRE content for indicators whether each change is positive or negative.\n2. Look for explicit keywords: \"up\", \"down\", \"increased\", \"decreased\", \"gain\", \"loss\", \"rose\", \"fell\".\n3. Look for color indicators in text, class names, or inline styles: \"red\", \"green\", \"text-red\", \"text-green\", \"color: red\", \"color: green\".\n4. Look for arrow or icon descriptions or characters: \"↑\", \"↑\", \"down\", \"arrow-up\", \"arrow-down\", \"fa-arrow-down\".\n5. Check the main price display area and any summary nearby for directional words or icons.\n6. If an indicator signals DOWN/RED/↓ -> the percentage must be NEGATIVE (prefix with -).\n If an indicator signals UP/GREEN/↑ -> the percentage must be POSITIVE (no - sign).\n7. If multiple contradictory indicators are present near the same number, prioritize: \n (a) explicit arrow/icon near the number, (b) color/class name near the number, (c) textual phrase in the same sentence/clause.\n\nInformation to extract:\n- Coin name: {{ $('For Each Coin').item.json.coin }}\n- Current timestamp in UTC (use current time when generating response)\n- Current coin price in USD (main price display)\n- Price change over the last 1 hour with correct sign (+/-)\n- Price change over the last 24 hours with correct sign (+/-)\n- Price change over the last 7 days with correct sign (+/-)\n- Market capitalization in USD (convert B/T to full numbers)\n- 24-hour trading volume in USD (convert B/M to full numbers)\n\nEXAMPLE CONVERSIONS (MUST follow exactly):\n- $2.21T -> 2210000000000\n- $66.2B -> 66200000000\n- $1.5M -> 1500000\n\nFORMATTING RULES (MANDATORY):\n1. Return ONLY a single valid JSON object, nothing else.\n2. Timestamp (ts) must be ISO 8601 UTC format (e.g., 2025-10-16T07:41:38Z).\n3. All numeric values must be plain numbers (no $, commas, % signs, not strings).\n - e.g., change_1h: -0.5 (not \"-0.5%\" and not \"-0.5\")\n4. For percentage fields (change_1h, change_24h, change_7d) return decimal numbers (e.g., -1.4 or 0.5).\n5. If a data point is not found after thorough search, set it to null.\n6. Do NOT output any additional keys beyond the required fields.\n7. Do NOT include code fences, explanations, backticks, or any surrounding text.\n8. The output must be directly parseable via JSON.parse().\n\nIMPORTANT: Output ONLY the single JSON object. DO NOT add explanations or extra text. The output must be VALID JSON parsable by JSON.parse().",
"options": {
"systemMessage": "=You are a cryptocurrency data extraction assistant specialized in analyzing CoinGecko-like website content.\n\nYour task is to extract coin information from the provided HTML/text content and return it in a precise JSON format as requested by the user.\n\nRequired output format example:\n{\n \"coin\": \"bitcoin\",\n \"ts\": \"2025-10-16T07:41:38Z\",\n \"price\": 110893,\n \"change_1h\": -0.5,\n \"change_24h\": -1.4,\n \"change_7d\": -9.1,\n \"market_cap\": 2210211755802,\n \"volume_24h\": 66206993665\n}\n\nCritical extraction rules:\n1. Timestamp (ts) must be in ISO 8601 UTC format.\n2. All numbers must be pure numeric values without currency symbols, commas, or percent signs.\n3. For percentage changes (change_1h, change_24h, change_7d):\n - Find the table with \"1h | 24h | 7d | 14d | 30d | 1y\" or similar.\n - Determine sign using visual/textual indicators: colors (green/red), arrows (↑/↓), class names (text-green/text-red), and directional words (up/down/increased/decreased).\n - If indicator = negative (red/down/↓) => add negative sign.\n - If indicator = positive (green/up/↑) => positive number (no sign).\n - Return as decimal numbers (e.g., -1.4 or 0.5).\n4. Convert market cap and volume units to full numbers (T/B/M/K) exactly as the Example Conversions.\n5. If any field cannot be found after thorough search, return null for that field.\n6. Return only the JSON object — do not add any extra text.\n7. Do not output code blocks, backticks, logs, or surrounding commentary.\n\nIf there are conflicting indicators, prioritize explicit nearby arrow/icon and class/style attributes above textual summary. If no clear indicator exists for a specific change, return the numeric magnitude as found but set its sign to null (or use the best inference from nearby sentences). Always prefer conservative (null) over guessing when uncertain."
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2.2
},
{
"id": "97b97f73-ff5e-4c68-ad98-c92240e36f1f",
"name": "## 🔄 步骤 3: 智能网站抓取",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1696,
2224
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"id": "858IDdJ9nuIsYkCZ",
"name": "Hendra Gemini"
}
},
"typeVersion": 1
},
{
"id": "e4935000-ecc3-4f40-8506-dde0e1610134",
"name": "逐个处理每个网站以防止 IP 被封:",
"type": "n8n-nodes-base.dataTable",
"position": [
944,
2704
],
"parameters": {
"limit": {},
"operation": "get",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "b3ek8fMoxaftXS9A",
"cachedResultUrl": "/projects/I8J05gT6Zk37Q2dE/datatables/b3ek8fMoxaftXS9A",
"cachedResultName": "Coin Prices"
}
},
"typeVersion": 1
},
{
"id": "f2758f49-05b4-448c-bf79-05ac8a55784b",
"name": "**遍历项目:** 逐个处理网站,内置延迟",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1392,
2704
],
"parameters": {
"text": "=COIN: {{$json.coin}}\nLATEST:\n- ts: {{$json.latest.ts}}\n- price: {{$json.latest.price}}\n- change_1h: {{ $json.latest.change_1h }}\n- change_24h: {{ $json.latest.change_24h }}\n\nSERIES (ISO, price) every ~30m, oldest→newest:\n{{$json.series}}\n\nTask: Produce the STRICT JSON per system prompt.",
"options": {
"systemMessage": "You analyze short-term crypto price action for beginners. Output STRICT JSON only.\n\nGiven the last ~48h price series and latest snapshot, estimate likely direction windows for the NEXT 24 HOURS.\n\nReturn this JSON:\n{\n \"direction_6h\": \"up|down|flat\",\n \"direction_12h\": \"up|down|flat\",\n \"direction_24h\": \"up|down|flat\",\n \"best_up_window\": {\"start_iso\":\"\", \"end_iso\":\"\"} or null,\n \"best_down_window\": {\"start_iso\":\"\", \"end_iso\":\"\"} or null,\n \"confidence\": \"low|medium|high\"\n}\n\nRules:\n- Use only provided data.\n- If signals mixed → prefer \"flat\" and reduce confidence.\n- best_*_window should be a contiguous window inside the next 24h from latest.ts (estimate based on recent momentum/volatility).\n- JSON only. No markdown, no code fences."
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2.2
},
{
"id": "861691d9-daf8-4d28-b51b-9dc3a7d9fa0b",
"name": "**抓取网站:** 从每个商家网站下载 HTML 内容",
"type": "n8n-nodes-base.code",
"position": [
1744,
2704
],
"parameters": {
"jsCode": "// Build combined HTML email (HTML only, GMT-flex)\n// Reads GMT offset from node \"Recipient Email\" -> field `timezone`\n// Accepted: \"+8\", \"8\", \"GMT+7\", \"UTC+5:30\", \"-3\", \"-03:30\", 8\n\nfunction safeParse(x) {\n if (!x && x !== 0) return {};\n if (typeof x === 'object') return x;\n try { return JSON.parse(x); } catch { return {}; }\n}\nfunction fmtUSD(n) {\n if (n == null || isNaN(n)) return '-';\n return new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 }).format(Number(n));\n}\nfunction dirWord(d) {\n const x = String(d || '').toLowerCase();\n if (x === 'up') return 'likely to rise';\n if (x === 'down') return 'likely to fall';\n return 'flat';\n}\nfunction confWord(c) {\n const x = String(c || '').toLowerCase();\n if (x === 'high') return 'high';\n if (x === 'medium') return 'medium';\n return 'low';\n}\n\n// ---- GMT offset helpers ----\nfunction parseGmtOffset(raw) {\n if (raw === undefined || raw === null || raw === '') return { minutes: 480, label: \"GMT+08:00\" }; // default +8\n let s = String(raw).trim().toUpperCase(); // \"GMT+7\", \"UTC+5:30\", \"+8\", \"8\", \"-03:30\"\n s = s.replace(/^GMT|^UTC/, '');\n if (s === '') s = '+0';\n if (!/^[-+]/.test(s)) s = (Number(s) >= 0 ? '+' : '') + s; // allow \"8\" -> \"+8\"\n\n const m = s.match(/^([+-])(\\d{1,2})(?::?(\\d{2}))?$/);\n if (!m) return { minutes: 480, label: \"GMT+08:00\" };\n const sign = m[1] === '-' ? -1 : 1;\n const hh = Math.min(14, Math.max(0, parseInt(m[2], 10)));\n const mm = m[3] ? Math.min(59, Math.max(0, parseInt(m[3], 10))) : 0;\n const minutes = sign * (hh * 60 + mm);\n const lab = `GMT${sign === 1 ? '+' : '-'}${String(hh).padStart(2,'0')}:${String(mm).padStart(2,'0')}`;\n return { minutes, label: lab };\n}\n\nfunction toLocalHHMM(iso, offsetMin, label) {\n if (!iso) return null;\n try {\n const d = new Date(iso);\n const local = new Date(d.getTime() + offsetMin * 60000);\n const t = local.toISOString().substring(11,16); // HH:MM\n return `${t} ${label}`;\n } catch { return null; }\n}\nfunction fmtWindowLocal(w, offsetMin, label) {\n if (!w || !w.start_iso || !w.end_iso) return null;\n const s = toLocalHHMM(w.start_iso, offsetMin, label);\n const e = toLocalHHMM(w.end_iso, offsetMin, label);\n return (s && e) ? `${s} – ${e}` : null;\n}\n\n// ---- read recipient tz ----\nlet tzRaw = null;\ntry { tzRaw = $('Recipient Email + Timezone').first().json.timezone ?? null; } catch {}\nconst tz = parseGmtOffset(tzRaw);\nconst tzLabel = tz.label;\nconst tzMinutes = tz.minutes;\n\n// ---- build per-coin HTML cards ----\nconst cards = items.map(it => {\n const raw = it.json && ('output' in it.json) ? it.json.output : it.json;\n const o = safeParse(raw);\n\n const coin = (o.coin || '').toUpperCase() || 'UNKNOWN';\n const price = o.latest?.price ?? o.price ?? null;\n const ch1 = (o.latest?.change_1h ?? o.change_1h);\n const ch24 = (o.latest?.change_24h ?? o.change_24h);\n const d6 = o.direction_6h || 'flat';\n const d12 = o.direction_12h || 'flat';\n const d24 = o.direction_24h || 'flat';\n const upWin = fmtWindowLocal(o.best_up_window, tzMinutes, tzLabel);\n const downWin = fmtWindowLocal(o.best_down_window, tzMinutes, tzLabel);\n const conf = confWord(o.confidence);\n\n const html = [\n `<div style=\"padding:12px;border-radius:8px;border:1px solid #e6e6e6;margin-bottom:10px;background:#ffffff;\">`,\n ` <div style=\"display:flex;justify-content:space-between;align-items:center\">`,\n ` <div style=\"font-weight:700;font-size:16px;\">${coin}</div>`,\n ` <div style=\"font-size:14px;color:#333\">Price: $${fmtUSD(price)}</div>`,\n ` </div>`,\n ` <div style=\"margin-top:8px;font-size:13px;color:#333\">`,\n ` <div><strong>Forecast</strong>: 6h — ${dirWord(d6)}, 12h — ${dirWord(d12)}, 24h — ${dirWord(d24)}</div>`,\n (typeof ch1 !== 'undefined' && ch1 !== null) ? ` <div>1h change: ${Number(ch1)}%</div>` : ``,\n (typeof ch24 !== 'undefined' && ch24 !== null) ? ` <div>24h change: ${Number(ch24)}%</div>` : ``,\n upWin ? ` <div style=\"margin-top:6px;color:green;\"><strong>Favorable for upside:</strong> ${upWin}</div>` : ``,\n downWin ? ` <div style=\"margin-top:6px;color:red;\"><strong>Prone to downside:</strong> ${downWin}</div>` : ``,\n ` <div style=\"margin-top:8px;font-size:12px;color:#666;\"><em>Model confidence: ${conf}</em></div>`,\n ` </div>`,\n `</div>`\n ].filter(Boolean).join('\\n');\n\n return html;\n});\n\n// fallback if empty\nif (cards.length === 0) {\n cards.push(`<div style=\"padding:12px;border-radius:8px;border:1px solid #e6e6e6;background:#fff;\">No forecast available.</div>`);\n}\n\n// header time in recipient's GMT\nconst now = new Date();\nconst nowLocal = new Date(now.getTime() + tzMinutes * 60000);\nconst nowLabel = `${nowLocal.toISOString().substring(0,10)} ${nowLocal.toISOString().substring(11,19)} ${tzLabel}`;\n\n// final HTML\nconst body_html = `\n<div style=\"font-family:Arial,Helvetica,sans-serif;background:#f7f7f7;padding:16px;\">\n <div style=\"max-width:700px;margin:0 auto;\">\n <div style=\"color:#111;line-height:1.4;\">\n <h2 style=\"margin:0 0 8px 0;font-size:18px;\">Crypto Forecast — ${nowLabel}</h2>\n <div style=\"margin-bottom:10px;color:#666;font-size:12px;\">Note: this is not financial advice. For educational purposes only.</div>\n </div>\n <div>\n ${cards.join('<div style=\"height:8px\"></div>')}\n </div>\n </div>\n</div>`.trim();\n\nreturn [{ json: { body_html } }];"
},
"typeVersion": 2
},
{
"id": "9efa28ee-6359-4488-833c-509b245a5618",
"name": "**等待节点:** 防止速率限制和 IP 封禁",
"type": "n8n-nodes-base.gmail",
"position": [
1968,
2704
],
"webhookId": "22e7450f-fa5c-4aed-9849-f394f8514fe8",
"parameters": {
"sendTo": "<your_recipient_email>",
"message": "={{ $json.body_html }}",
"options": {},
"subject": "=Crypto Forecast — {{$json.body_html.match(/GMT[+-]\\d{2}:\\d{2}/)?.[0] || 'GMT'}} "
},
"credentials": {
"gmailOAuth2": {
"id": "xFrtY2kQTvgGH9NM",
"name": "Hendra Gmail"
}
},
"typeVersion": 2.1
},
{
"id": "5cbf47a5-6b94-477f-8a89-61a8d23ae24d",
"name": "**错误处理:** 即使某些网站失败也能继续处理",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1392,
2928
],
"parameters": {
"options": {}
},
"credentials": {
"googlePalmApi": {
"id": "858IDdJ9nuIsYkCZ",
"name": "Hendra Gemini"
}
},
"typeVersion": 1
},
{
"id": "8bbe2f83-b73d-4caa-af47-34921c50a4cb",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1600,
1648
],
"parameters": {
"color": 5,
"width": 400,
"height": 304,
"content": "**关键:** 批处理和延迟对于大规模可靠运行至关重要"
},
"typeVersion": 1
},
{
"id": "7ef7e9a1-effa-4509-9199-e05b055eff17",
"name": "便签4",
"type": "n8n-nodes-base.stickyNote",
"position": [
336,
1904
],
"parameters": {
"color": 5,
"width": 272,
"height": 256,
"content": "## 📧 步骤 4: 电子邮件提取与导出"
},
"typeVersion": 1
},
{
"id": "e9245fdd-b8b2-4cd1-90b9-82f56ee78217",
"name": "最终处理流程:",
"type": "n8n-nodes-base.set",
"position": [
720,
2704
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "dc601405-466f-4456-9377-f1cb1a34562c",
"name": "recipientEmail",
"type": "string",
"value": "hoktarizal@student.ciputra.ac.id"
},
{
"id": "45b95be4-4a9c-4ad9-9fac-e28b7c4438b1",
"name": "timezone",
"type": "string",
"value": "GMT+0"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "3d971d2b-6242-4c71-8ffa-dade92014e26",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
2528
],
"parameters": {
"color": 3,
"width": 2080,
"height": 640,
"content": "1. **提取电子邮件:** JavaScript 正则表达式在网站 HTML 中查找所有电子邮件地址"
},
"typeVersion": 1
},
{
"id": "9f8bdde9-e6e5-4d8a-bef5-9796da792d1a",
"name": "便签5",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
1504
],
"parameters": {
"color": 4,
"width": 2080,
"height": 944,
"content": "2. **过滤空值:** 移除未找到电子邮件的网站"
},
"typeVersion": 1
},
{
"id": "61a3d05f-c356-45b3-b398-acdda74694bd",
"name": "3. **拆分:** 将电子邮件数组转换为单独的项目",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
512,
2704
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 18
}
]
}
},
"typeVersion": 1.2
},
{
"id": "eedd1f06-4db7-4cee-b5d3-e123de9ff94d",
"name": "便签6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-544,
1680
],
"parameters": {
"width": 592,
"height": 672,
"content": "4. **去重:** 对所有来源进行最终去重"
},
"typeVersion": 1
},
{
"id": "bd21e39f-ba2b-41de-908c-5d313a69b9db",
"name": "便签7",
"type": "n8n-nodes-base.stickyNote",
"position": [
656,
1888
],
"parameters": {
"color": 5,
"height": 272,
"content": "5. **添加到表格:** 将干净的电子邮件列表导出到 Google Sheets"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"Make it JSON": {
"main": [
[
{
"node": "Forecast Next 24h (LLM)",
"type": "main",
"index": 0
}
]
]
},
"For Each Coin": {
"main": [
[
{
"node": "Insert Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Scrape CoinGecko (Decodo)",
"type": "main",
"index": 0
}
]
]
},
"Configure Coins": {
"main": [
[
{
"node": "Build Coin Items",
"type": "main",
"index": 0
}
]
]
},
"Build Coin Items": {
"main": [
[
{
"node": "For Each Coin",
"type": "main",
"index": 0
}
]
]
},
"Gemini for Parsing": {
"ai_languageModel": [
[
{
"node": "Parse Coin Metrics (LLM)",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Load Data Last 48h": {
"main": [
[
{
"node": "Make it JSON",
"type": "main",
"index": 0
}
]
]
},
"Gemini for Forecast": {
"ai_languageModel": [
[
{
"node": "Forecast Next 24h (LLM)",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Convert Output to JSON": {
"main": [
[
{
"node": "For Each Coin",
"type": "main",
"index": 0
}
]
]
},
"Forecast Next 24h (LLM)": {
"main": [
[
{
"node": "Build Email (HTML + Text)",
"type": "main",
"index": 0
}
]
]
},
"Parse Coin Metrics (LLM)": {
"main": [
[
{
"node": "Convert Output to JSON",
"type": "main",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "Forecast Next 24h (LLM)",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Build Email (HTML + Text)": {
"main": [
[
{
"node": "Send Email (Gmail)",
"type": "main",
"index": 0
}
]
]
},
"Scrape CoinGecko (Decodo)": {
"main": [
[
{
"node": "Parse Coin Metrics (LLM)",
"type": "main",
"index": 0
}
]
]
},
"Recipient Email + Timezone": {
"main": [
[
{
"node": "Load Data Last 48h",
"type": "main",
"index": 0
}
]
]
},
"Schedule: Every 30m (Scrape & Log)": {
"main": [
[
{
"node": "Configure Coins",
"type": "main",
"index": 0
}
]
]
},
"Schedule: 18:00 Daily (Forecast Email)": {
"main": [
[
{
"node": "Recipient Email + Timezone",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 加密货币交易
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
每周SEO监控清单审计至Google Sheets(使用Gemini和Decodo)
每周SEO监控清单审计至Google Sheets(使用Gemini和Decodo)
Set
Code
Merge
+9
19 节点Fahmi Fahreza
市场调研
使用Decodo、Gemini AI和Google Sheets抓取、结构化并存储新闻数据
使用Decodo、Gemini AI和Google Sheets抓取、结构化并存储新闻数据
Set
Wait
Crypto
+9
18 节点Fahmi Fahreza
市场调研
在可视化参考库中探索n8n节点
在可视化参考库中探索n8n节点
If
Ftp
Set
+93
113 节点I versus AI
其他
通过 Telegram 使用 Finnhub API 和 Gemini AI 获取 IPO 日历提醒
通过 Telegram 使用 Finnhub API 和 Gemini AI 获取 IPO 日历提醒
Set
Code
Telegram
+6
15 节点Malik Hashir
加密货币交易
使用Gemini AI和Decodo爬虫自动匹配简历与职位
使用Gemini AI和Decodo爬虫自动匹配简历与职位
Set
Gmail
Merge
+9
20 节点Fahmi Fahreza
人力资源
使用Gemini-2.0-Flash发送AI增强经济日历提醒到Telegram
使用Gemini-2.0-Flash发送AI增强经济日历提醒到Telegram
Set
Code
Telegram
+6
17 节点Malik Hashir
加密货币交易