8
n8n 中文网amn8n.com

使用Apify监控Reddit、Instagram和TikTok社交媒体趋势

高级

这是一个Miscellaneous, Multimodal AI领域的自动化工作流,包含 30 个节点。主要使用 Code, Wait, Gmail, HttpRequest, ScheduleTrigger 等节点。 使用Apify监控Reddit、Instagram和TikTok社交媒体趋势

前置要求
  • Google 账号和 Gmail API 凭证
  • 可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "393ca9e36a1f81b0f643c72792946a5fe5e49eb4864181ba4032e5a408278263",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "1e128094-fe0f-484a-98ae-9e2868ff6561",
      "name": "Reddit 发帖",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Post the request on apify",
      "position": [
        112,
        -80
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/trudax~reddit-scraper-lite/runs",
        "method": "POST",
        "options": {
          "timeout": 60000
        },
        "jsonBody": "{\n  \"searches\": [\"trottinette\"],\n  \"searchPosts\": true,\n  \"searchComments\": false,\n  \"searchCommunities\": false,\n  \"searchUsers\": false,\n  \"sort\": \"top\",\n  \"time\": \"month\",\n  \"maxItems\": 50,\n  \"maxPostCount\": 25,\n  \"maxComments\": 5,\n  \"scrollTimeout\": 40,\n  \"proxy\": {\n    \"useApifyProxy\": true,\n    \"apifyProxyGroups\": [\"RESIDENTIAL\"]\n  }\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "jAy18eDHHP2ZoGrH",
          "name": "Apify"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "93d07f06-bb90-47dc-9f70-2ea9f541d379",
      "name": "Reddit 获取",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Get the package with the info",
      "position": [
        800,
        -80
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/datasets/{{ $json.data.defaultDatasetId }}/items",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "jAy18eDHHP2ZoGrH",
          "name": "Apify"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "cab481d9-dc65-4d46-95c3-bd33b307d662",
      "name": "Instagram 发帖",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Post the request on apify",
      "position": [
        112,
        864
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/apify~instagram-scraper/runs",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n  \"directUrls\": [\n    \"https://www.instagram.com/explore/tags/trottinette/\"\n  ],\n  \"resultsType\": \"posts\",\n  \"resultsLimit\": 15,\n  \"onlyPostsNewerThan\": \"7 days\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "jAy18eDHHP2ZoGrH",
          "name": "Apify"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "0cfd5977-c970-40a1-bd02-e2627068d6c5",
      "name": "Instagram 获取",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Get the package with the info",
      "position": [
        800,
        864
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/datasets/{{ $json.data.defaultDatasetId }}/items",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "jAy18eDHHP2ZoGrH",
          "name": "Apify"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "88ff5cb5-0bef-4063-ad46-ed386e5a7ce1",
      "name": "TikTok 发帖",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Post the request on apify",
      "position": [
        96,
        1776
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/acts/clockworks~tiktok-scraper/runs",
        "method": "POST",
        "options": {},
        "jsonBody": "{\n  \"hashtags\": [\"trottinette\"],\n  \"resultsPerPage\": 20,\n  \"oldestPostDateUnified\": \"7 days\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "jAy18eDHHP2ZoGrH",
          "name": "Apify"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "41ad9cb1-b1c1-4443-b424-51270cff20c9",
      "name": "TikTok 获取",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Get the package with the info",
      "position": [
        768,
        1776
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/datasets/{{ $json.data.defaultDatasetId }}/items",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "jAy18eDHHP2ZoGrH",
          "name": "Apify"
        }
      },
      "notesInFlow": true,
      "typeVersion": 4.2
    },
    {
      "id": "a13acf5b-cd13-405b-b0d6-d9590d9b9965",
      "name": "发送消息",
      "type": "n8n-nodes-base.gmail",
      "notes": "Send dahsboard with top 10 of the most engaging post ",
      "position": [
        3216,
        832
      ],
      "webhookId": "57a6f413-f0fa-4e3b-8576-96fce6d1a87f",
      "parameters": {
        "sendTo": "Your cool mail",
        "message": "=<!DOCTYPE html>\n<html lang=\"fr\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Trends Social Media - Trottinette</title>\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n\n        body {\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n            line-height: 1.6;\n            color: #333;\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            min-height: 100vh;\n            padding: 20px;\n        }\n\n        .newsletter-container {\n            max-width: 1200px;\n            margin: 0 auto;\n            background: white;\n            border-radius: 20px;\n            overflow: hidden;\n            box-shadow: 0 20px 40px rgba(0,0,0,0.1);\n        }\n\n        .header {\n            background: linear-gradient(135deg, #ff6b6b, #ee5a24);\n            color: white;\n            padding: 40px 30px;\n            text-align: center;\n        }\n\n        .header h1 {\n            font-size: 2.8em;\n            margin-bottom: 10px;\n            font-weight: 700;\n        }\n\n        .header p {\n            font-size: 1.2em;\n            opacity: 0.9;\n            margin-bottom: 15px;\n        }\n\n        .keyword-badge {\n            display: inline-block;\n            background: rgba(255,255,255,0.2);\n            color: white;\n            padding: 8px 20px;\n            border-radius: 20px;\n            font-size: 1em;\n            font-weight: 600;\n            backdrop-filter: blur(10px);\n        }\n\n        .stats-overview {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n            gap: 20px;\n            padding: 30px;\n            background: #f8f9fa;\n        }\n\n        .stat-card {\n            background: white;\n            padding: 20px;\n            border-radius: 15px;\n            text-align: center;\n            box-shadow: 0 5px 15px rgba(0,0,0,0.08);\n            border-left: 4px solid;\n        }\n\n        .stat-card.high { border-left-color: #ff4757; }\n        .stat-card.medium { border-left-color: #ffa502; }\n        .stat-card.low { border-left-color: #70a1ff; }\n\n        .stat-number {\n            font-size: 2.5em;\n            font-weight: bold;\n            margin-bottom: 5px;\n        }\n\n        .stat-label {\n            color: #666;\n            font-size: 0.9em;\n            text-transform: uppercase;\n            letter-spacing: 1px;\n        }\n\n        .table-container {\n            margin: 30px;\n            background: white;\n            border-radius: 15px;\n            overflow: hidden;\n            box-shadow: 0 10px 25px rgba(0,0,0,0.1);\n        }\n\n        .table-header {\n            background: linear-gradient(135deg, #2c3e50, #34495e);\n            color: white;\n            padding: 25px 30px;\n            text-align: center;\n        }\n\n        .table-title {\n            font-size: 1.8em;\n            font-weight: 600;\n            margin-bottom: 5px;\n        }\n\n        .table-subtitle {\n            opacity: 0.8;\n            font-size: 1em;\n        }\n\n        .trends-table {\n            width: 100%;\n            border-collapse: collapse;\n            font-size: 0.95em;\n        }\n\n        .trends-table th {\n            background: #f8f9fa;\n            padding: 15px 12px;\n            text-align: left;\n            font-weight: 600;\n            color: #555;\n            border-bottom: 2px solid #dee2e6;\n            text-transform: uppercase;\n            font-size: 0.8em;\n            letter-spacing: 0.5px;\n        }\n\n        .trends-table td {\n            padding: 15px 12px;\n            border-bottom: 1px solid #eee;\n            vertical-align: middle;\n            text-align: center;\n        }\n\n        .trends-table td:nth-child(3),\n        .trends-table td:nth-child(4) {\n            text-align: left;\n        }\n\n        .trends-table tr:nth-child(even) {\n            background-color: #f8f9fa;\n        }\n\n        .trends-table tr:hover {\n            background-color: #e3f2fd;\n            transform: scale(1.01);\n            transition: all 0.2s ease;\n        }\n\n        .rank-number {\n            font-weight: bold;\n            font-size: 1.2em;\n            color: #2c3e50;\n        }\n\n        .rank-number.top3 {\n            color: #ff6b6b;\n            font-size: 1.4em;\n        }\n\n        .source-badge {\n            padding: 6px 12px;\n            border-radius: 20px;\n            font-size: 0.8em;\n            font-weight: 600;\n            text-transform: uppercase;\n            letter-spacing: 0.5px;\n        }\n\n        .source-badge.tiktok {\n            background: #ff0050;\n            color: white;\n        }\n\n        .source-badge.reddit {\n            background: #ff4500;\n            color: white;\n        }\n\n        .source-badge.instagram {\n            background: #e4405f;\n            color: white;\n        }\n\n        .level-badge {\n            padding: 4px 10px;\n            border-radius: 15px;\n            font-size: 0.75em;\n            font-weight: 600;\n            text-transform: uppercase;\n        }\n\n        .level-badge.high {\n            background: #ff4757;\n            color: white;\n        }\n\n        .level-badge.medium {\n            background: #ffa502;\n            color: white;\n        }\n\n        .level-badge.low {\n            background: #70a1ff;\n            color: white;\n        }\n\n        .content-text {\n            max-width: 250px;\n            overflow: hidden;\n            text-overflow: ellipsis;\n            white-space: nowrap;\n            color: #555;\n            line-height: 1.4;\n        }\n\n        .author-link {\n            color: #667eea;\n            text-decoration: none;\n            font-weight: 600;\n        }\n\n        .author-link:hover {\n            text-decoration: underline;\n        }\n\n        .score-number {\n            font-weight: bold;\n            color: #2c3e50;\n            font-size: 1.1em;\n        }\n\n        .footer {\n            text-align: center;\n            padding: 30px;\n            background: #f8f9fa;\n            color: #666;\n            border-top: 1px solid #eee;\n        }\n\n        .footer p {\n            margin-bottom: 10px;\n        }\n\n        .date-info {\n            font-size: 0.9em;\n            color: #888;\n        }\n\n        @media (max-width: 768px) {\n            .newsletter-container {\n                margin: 10px;\n                border-radius: 15px;\n            }\n            \n            .header {\n                padding: 30px 20px;\n            }\n            \n            .header h1 {\n                font-size: 2.2em;\n            }\n            \n            .table-container {\n                margin: 20px;\n                overflow-x: auto;\n            }\n            \n            .trends-table {\n                min-width: 800px;\n            }\n            \n            .stats-overview {\n                padding: 20px;\n                grid-template-columns: 1fr;\n            }\n        }\n    </style>\n</head>\n<body>\n    <div class=\"newsletter-container\">\n        <!-- Header -->\n        <header class=\"header\">\n            <h1>📊 Récapitulatif par Hashtag</h1>\n            <p>Scores totaux par réseau social</p>\n            <div class=\"keyword-badge\">Période : 30 derniers jours</div>\n        </header>\n\n        <!-- Hashtag Summary Table -->\n        <section class=\"table-container\" style=\"margin: 30px 30px 0 30px;\">\n            <table class=\"trends-table\">\n                <thead>\n                    <tr>\n                        <th>Hashtag</th>\n                        <th>TikTok</th>\n                        <th>Reddit</th>\n                        <th>Instagram</th>\n                        <th>Score Total</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td>\n                            <span style=\"font-weight: bold; color: #ff6b6b; font-size: 1.1em;\">#trottinette</span>\n                        </td>\n                        <td>\n                            <span class=\"score-number\">{{ $('Classement global').all().filter(item => item.json.source === 'TikTok').reduce((sum, item) => sum + item.json.score, 0).toLocaleString('fr-FR') }}</span>\n                        </td>\n                        <td>\n                            <span class=\"score-number\">{{ $('Classement global').all().filter(item => item.json.source === 'Reddit').reduce((sum, item) => sum + item.json.score, 0).toLocaleString('fr-FR') }}</span>\n                        </td>\n                        <td>\n                            <span class=\"score-number\">{{ $('Classement global').all().filter(item => item.json.source === 'Instagram').reduce((sum, item) => sum + item.json.score, 0).toLocaleString('fr-FR') }}</span>\n                        </td>\n                        <td>\n                            <span class=\"score-number\" style=\"color: #ff6b6b; font-size: 1.2em;\">{{ $('Classement global').all().reduce((sum, item) => sum + item.json.score, 0).toLocaleString('fr-FR') }}</span>\n                        </td>\n                    </tr>\n                </tbody>\n            </table>\n        </section>\n\n        <!-- Main Table -->\n        <section class=\"table-container\">\n            <div class=\"table-header\">\n                <div class=\"table-title\">#Trottinette</div>\n                <div class=\"table-subtitle\"></div>\n            </div>\n            \n            <table class=\"trends-table\">\n                <thead>\n                    <tr>\n                        <th>Rang</th>\n                        <th>Source</th>\n                        <th>Auteur</th>\n                        <th>Contenu</th>\n                        <th>Score</th>\n                        <th>Level</th>\n                    </tr>\n                </thead>\n                <tbody>\n                    {{ $('Classement global').all().map(item => `\n                        <tr onclick=\"window.open('${item.json.url}', '_blank')\" style=\"cursor: pointer;\">\n                            <td>\n                                <span class=\"rank-number ${item.json.rank <= 3 ? 'top3' : ''}\">${item.json.rank}</span>\n                            </td>\n                            <td>\n                                <span class=\"source-badge ${item.json.source.toLowerCase()}\">${item.json.source}</span>\n                            </td>\n                            <td>\n                                <a href=\"${item.json.url}\" target=\"_blank\" class=\"author-link\">${item.json.author}</a>\n                            </td>\n                            <td>\n                                <div class=\"content-text\" title=\"${item.json.content}\">${item.json.content}</div>\n                            </td>\n                            <td>\n                                <span class=\"score-number\">${item.json.score.toLocaleString('fr-FR')}</span>\n                            </td>\n                            <td>\n                                <span class=\"level-badge ${item.json.level.toLowerCase()}\">${item.json.level}</span>\n                            </td>\n                        </tr>\n                    `).join('') }}\n                </tbody>\n            </table>\n        </section>\n\n        <!-- Footer -->\n        <footer class=\"footer\">\n            <p><strong>Social Media Trends Dashboard</strong></p>\n            <p>Analyse automatisée des contenus par mot-clé</p>\n            <div class=\"date-info\">\n                <p>Généré le {{ new Date().toLocaleDateString('fr-FR', { \n                    year: 'numeric', \n                    month: 'long', \n                    day: 'numeric', \n                    hour: '2-digit', \n                    minute: '2-digit' \n                }) }}</p>\n            </div>\n        </footer>\n    </div>\n</body>\n</html>",
        "options": {},
        "subject": "Social media monitoring"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "F6YsSmraPe7v98FF",
          "name": "Gmail account"
        }
      },
      "executeOnce": true,
      "notesInFlow": true,
      "typeVersion": 2.1
    },
    {
      "id": "e864fbee-252a-46db-ac74-b515fbd566a4",
      "name": "全球排名",
      "type": "n8n-nodes-base.code",
      "notes": "Sorts all posts + give them points depending on their likes + comments",
      "position": [
        2048,
        832
      ],
      "parameters": {
        "jsCode": "// Code pour nœud Code n8n - Classement unifié multi-plateformes\n// Récupérer les données des 3 nœuds précédents\nconst redditData = $('Sort Reddit').all();     // Nœud Reddit\nconst instagramData = $('Sort Instagram').all(); // Nœud Instagram  \nconst tiktokData = $('Sort TikTok').all();    // Nœud TikTok\n\nconsole.log(`📥 Récupération des données:`);\nconsole.log(`- Reddit: ${redditData.length} posts`);\nconsole.log(`- Instagram: ${instagramData.length} posts`);\nconsole.log(`- TikTok: ${tiktokData.length} posts`);\n\n// Fonction pour déterminer le level basé sur le score\nfunction getLevel(score) {\n  if (score >= 10000) return 'High';\n  if (score >= 1000) return 'Medium';\n  return 'Low';\n}\n\n// Fonction pour extraire l'auteur selon la plateforme\nfunction getAuthor(item, source) {\n  switch(source) {\n    case 'TikTok':\n      // Extraire le nom d'utilisateur de l'URL TikTok\n      const tiktokMatch = item.url.match(/tiktok\\.com\\/@([^\\/]+)/);\n      return tiktokMatch ? `@${tiktokMatch[1]}` : '@inconnu';\n      \n    case 'Instagram': \n      // Extraire le nom d'utilisateur de l'URL Instagram\n      const instaMatch = item.url.match(/instagram\\.com\\/([^\\/]+)/);\n      return instaMatch ? `@${instaMatch[1]}` : '@inconnu';\n      \n    case 'Reddit':\n      // Extraire le subreddit de l'URL Reddit\n      const redditMatch = item.url.match(/\\/r\\/([^\\/]+)/);\n      return redditMatch ? `r/${redditMatch[1]}` : 'user_inconnu';\n      \n    default:\n      return 'inconnu';\n  }\n}\n\n// Transformer les données Reddit\nconst redditPosts = redditData.map(item => ({\n  json: {\n    source: 'Reddit',\n    keyword: 'trottinette',\n    author: getAuthor(item.json, 'Reddit'),\n    content: (item.json.title || 'Pas de titre').substring(0, 50) + '...',\n    score: item.json.score,\n    level: getLevel(item.json.score),\n    url: item.json.url,\n    originalRank: item.json.rank\n  }\n}));\n\n// Transformer les données Instagram  \nconst instagramPosts = instagramData.map(item => ({\n  json: {\n    source: 'Instagram',\n    keyword: 'trottinette', \n    author: getAuthor(item.json, 'Instagram'),\n    content: (item.json.caption || 'Pas de caption').substring(0, 50) + '...',\n    score: item.json.score,\n    level: getLevel(item.json.score),\n    url: item.json.url,\n    originalRank: item.json.rank\n  }\n}));\n\n// Transformer les données TikTok\nconst tiktokPosts = tiktokData.map(item => ({\n  json: {\n    source: 'TikTok',\n    keyword: 'trottinette',\n    author: getAuthor(item.json, 'TikTok'), \n    content: (item.json.caption || 'Pas de caption').substring(0, 50) + '...',\n    score: item.json.score,\n    level: getLevel(item.json.score),\n    url: item.json.url,\n    originalRank: item.json.rank\n  }\n}));\n\n// Combiner toutes les données\nconst allPosts = [...redditPosts, ...instagramPosts, ...tiktokPosts];\n\n// Trier par score décroissant\nconst sortedPosts = allPosts.sort((a, b) => b.json.score - a.json.score);\n\n// Ajouter le rang unifié\nconst rankedPosts = sortedPosts.map((post, index) => ({\n  json: {\n    ...post.json,\n    rank: index + 1\n  }\n}));\n\n// Prendre le top 15 (5 de chaque plateforme environ)\nconst topPosts = rankedPosts.slice(0, 15);\n\n// Statistiques par plateforme\nconst stats = {\n  total: topPosts.length,\n  bySource: {\n    Reddit: topPosts.filter(p => p.json.source === 'Reddit').length,\n    Instagram: topPosts.filter(p => p.json.source === 'Instagram').length,\n    TikTok: topPosts.filter(p => p.json.source === 'TikTok').length\n  },\n  byLevel: {\n    High: topPosts.filter(p => p.json.level === 'High').length,\n    Medium: topPosts.filter(p => p.json.level === 'Medium').length,\n    Low: topPosts.filter(p => p.json.level === 'Low').length\n  }\n};\n\n// Log pour debug\nconsole.log(`🏆 TOP ${topPosts.length} POSTS MULTI-PLATEFORMES - Mot-clé: \"trottinette\"`);\nconsole.log(`────────────────────────────────────────────────────────────────────────────────`);\n\ntopPosts.forEach((post) => {\n  const p = post.json;\n  console.log(`${p.rank}. [${p.source}] ${p.author} - Score: ${p.score.toLocaleString()} (${p.level})`);\n  console.log(`   Contenu: \"${p.content}\"`);\n  console.log(`   URL: ${p.url}`);\n  console.log(`   ────────────────────────────────────────────────────────────────────────────`);\n});\n\nconsole.log(`\\n📊 STATISTIQUES:`);\nconsole.log(`Total: ${stats.total} posts`);\nconsole.log(`Par plateforme: Reddit(${stats.bySource.Reddit}) | Instagram(${stats.bySource.Instagram}) | TikTok(${stats.bySource.TikTok})`);\nconsole.log(`Par niveau: High(${stats.byLevel.High}) | Medium(${stats.byLevel.Medium}) | Low(${stats.byLevel.Low})`);\n\n// Retourner le classement unifié\nreturn topPosts;"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "405bc5f7-f2d0-4eef-887c-e69d6619127a",
      "name": "Reddit 等待",
      "type": "n8n-nodes-base.wait",
      "notes": "Wait for the package to generate",
      "position": [
        448,
        -80
      ],
      "webhookId": "7664c458-0a6a-4863-bc46-22297467b4a1",
      "parameters": {
        "amount": 30
      },
      "notesInFlow": true,
      "typeVersion": 1.1
    },
    {
      "id": "47a890b1-7a6b-4571-a745-bcada1ad20cb",
      "name": "Instagram 等待",
      "type": "n8n-nodes-base.wait",
      "notes": "Wait for the package to generate",
      "position": [
        448,
        864
      ],
      "webhookId": "66faed20-9e41-46d9-951e-030be4ff6960",
      "parameters": {
        "amount": 30
      },
      "typeVersion": 1.1
    },
    {
      "id": "9f63457e-4f4f-41ee-9191-058f240f9292",
      "name": "TikTok 等待",
      "type": "n8n-nodes-base.wait",
      "notes": "Wait for the package to generate",
      "position": [
        416,
        1776
      ],
      "webhookId": "66faed20-9e41-46d9-951e-030be4ff6960",
      "parameters": {
        "amount": 30
      },
      "notesInFlow": true,
      "typeVersion": 1.1
    },
    {
      "id": "43375436-6063-4cb7-b0bd-f7889c34180b",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        -288
      ],
      "parameters": {
        "color": 3,
        "width": 1504,
        "height": 912,
        "content": "# Reddit"
      },
      "typeVersion": 1
    },
    {
      "id": "5d6bd690-1095-417b-84fc-73af7dd769a5",
      "name": "Reddit 排序",
      "type": "n8n-nodes-base.code",
      "notes": "Sorts posts",
      "position": [
        1120,
        -80
      ],
      "parameters": {
        "jsCode": "// Code pour nœud Code n8n - Classement Reddit avec output simplifié\n\n// Récupérer les données d'entrée\nconst items = $input.all();\n\n// Filtrer uniquement les posts (exclure les commentaires)\nconst posts = items.filter(item => item.json.dataType === 'post');\n\n// Calculer le score pour chaque post\nconst postsWithScore = posts.map(item => {\n  const post = item.json;\n  \n  // Calcul du score : 1 point par upvote + 2 points par commentaire\n  const upvoteScore = (post.upVotes || 0) * 1;\n  const commentScore = (post.numberOfComments || 0) * 2\n    ;\n  const totalScore = upvoteScore + commentScore;\n  \n  return {\n    json: {\n      url: post.url,\n      title: post.title || \"Pas de titre\",\n      body: post.body || \"Pas de contenu\",\n      score: totalScore,\n      // Données détaillées pour debug (optionnel)\n      details: {\n        upvotes: post.upVotes || 0,\n        comments: post.numberOfComments || 0,\n        community: post.communityName,\n        timestamp: post.createdAt\n      }\n    }\n  };\n});\n\n// Trier par score décroissant\nconst sortedPosts = postsWithScore.sort((a, b) => b.json.score - a.json.score);\n\n// Prendre les 10 meilleurs\nconst top10Posts = sortedPosts.slice(0, 10);\n\n// Ajouter un rang à chaque post\nconst rankedPosts = top10Posts.map((post, index) => ({\n  json: {\n    rank: index + 1,\n    url: post.json.url,\n    title: post.json.title,\n    body: post.json.body,\n    score: post.json.score\n  }\n}));\n\n// Log pour debug\nconsole.log(`🏆 TOP 10 POSTS REDDIT - Score = (upvotes×1) + (commentaires×2)`);\nconsole.log(`────────────────────────────────────────────────────────────`);\n\nrankedPosts.forEach((post, index) => {\n  const p = post.json;\n  const shortTitle = p.title.length > 60 ? p.title.substring(0, 60) + '...' : p.title;\n  const shortBody = p.body.length > 80 ? p.body.substring(0, 80) + '...' : p.body;\n  \n  console.log(`${p.rank}. Score: ${p.score}`);\n  console.log(`   URL: ${p.url}`);\n  console.log(`   Titre: \"${shortTitle}\"`);\n  console.log(`   Contenu: \"${shortBody}\"`);\n  console.log(`   ────────────────────────────────────────────────────────`);\n});\n\nconsole.log(`\\n📊 Analysé ${posts.length} posts au total`);\n\n// Retourner les résultats avec le format demandé\nreturn rankedPosts;"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "ae4b1108-7bbb-412a-9ba1-a2fe60738240",
      "name": "Instagram 排序",
      "type": "n8n-nodes-base.code",
      "notes": "Sorts posts",
      "position": [
        1136,
        864
      ],
      "parameters": {
        "jsCode": "// Code pour nœud Code n8n - Classement Instagram avec output simplifié\n\n// Récupérer les données d'entrée\nconst items = $input.all();\n\n// Filtrer uniquement les posts principaux (exclure les childPosts)\nconst posts = items.filter(item => {\n  const post = item.json;\n  return post.type && post.type !== 'carousel_item' && post.url && post.url.includes('/p/');\n});\n\n// Calculer le score pour chaque post\nconst postsWithScore = posts.map(item => {\n  const post = item.json;\n  \n  // Métriques de base (seulement likes et commentaires)\n  const likes = post.likesCount || 0;\n  const comments = post.commentsCount || 0;\n  \n  // Calcul du score simplifié : (likes * 1) + (comments * 2)\n  const likesScore = likes * 1;\n  const commentsScore = comments * 2;\n  const totalScore = likesScore + commentsScore;\n  \n  return {\n    json: {\n      url: post.url,\n      caption: post.caption || \"Pas de caption\",\n      score: totalScore,\n      // Données détaillées pour debug (optionnel)\n      details: {\n        likes: likes,\n        comments: comments,\n        type: post.type,\n        timestamp: post.timestamp\n      }\n    }\n  };\n});\n\n// Trier par score décroissant\nconst sortedPosts = postsWithScore.sort((a, b) => b.json.score - a.json.score);\n\n// Prendre les 10 meilleurs\nconst top10Posts = sortedPosts.slice(0, 10);\n\n// Ajouter un rang à chaque post\nconst rankedPosts = top10Posts.map((post, index) => ({\n  json: {\n    rank: index + 1,\n    url: post.json.url,\n    caption: post.json.caption,\n    score: post.json.score\n  }\n}));\n\n// Log pour debug\nconsole.log(`🏆 TOP 10 POSTS INSTAGRAM - Score = (likes×1) + (comments×2)`);\nconsole.log(`────────────────────────────────────────────────────────────`);\n\nrankedPosts.forEach((post, index) => {\n  const p = post.json;\n  const shortCaption = p.caption.length > 60 ? p.caption.substring(0, 60) + '...' : p.caption;\n  console.log(`${p.rank}. Score: ${p.score}`);\n  console.log(`   URL: ${p.url}`);\n  console.log(`   Caption: \"${shortCaption}\"`);\n  console.log(`   ────────────────────────────────────────────────────────`);\n});\n\nconsole.log(`\\n📊 Analysé ${posts.length} posts au total`);\n\n// Retourner les résultats avec le format demandé\nreturn rankedPosts;"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "f449974c-22d1-4ad4-9d81-112b59bbf333",
      "name": "TikTok 排序",
      "type": "n8n-nodes-base.code",
      "notes": "Sorts posts",
      "position": [
        1120,
        1776
      ],
      "parameters": {
        "jsCode": "// Code pour nœud Code n8n - Classement TikTok avec score personnalisé\n// Récupérer les données d'entrée\nconst items = $input.all();\n\n// Vérifier si on a des données\nif (!items || items.length === 0) {\n  console.log(\"❌ Aucune donnée d'entrée trouvée\");\n  return [];\n}\n\n// Extraire les posts TikTok du JSON\nlet posts = [];\n\n// Si les données sont dans un tableau à l'intérieur du premier item\nif (items[0] && items[0].json && Array.isArray(items[0].json)) {\n  posts = items[0].json;\n} \n// Si les données sont directement dans les items\nelse if (Array.isArray(items)) {\n  posts = items.map(item => item.json);\n}\n\nconsole.log(`📥 ${posts.length} posts TikTok trouvés`);\n\n// Calculer le score pour chaque post TikTok\nconst postsWithScore = posts.map((post, index) => {\n  // Métriques TikTok\n  const likes = post.diggCount || 0;\n  const comments = post.commentCount || 0;\n  const shares = post.shareCount || 0;\n  const views = post.playCount || 0;\n  \n  // Calcul du score : (likes * 1) + (comments * 2) + (shares * 3) + (views / 1000)\n  const likesScore = likes * 1;\n  const commentsScore = comments * 2;\n  const sharesScore = shares * 3;\n  const viewsScore = Math.floor(views / 1000);\n  const totalScore = likesScore + commentsScore + sharesScore + viewsScore;\n  \n  return {\n    json: {\n      url: post.webVideoUrl || `https://www.tiktok.com/@${post.authorMeta?.name}/video/${post.id}`,\n      caption: post.text || \"Pas de caption\",\n      score: totalScore,\n      // Données détaillées pour debug (optionnel)\n      details: {\n        likes: likes,\n        comments: comments,\n        shares: shares,\n        views: views,\n        author: post.authorMeta?.name || \"Inconnu\",\n        id: post.id\n      }\n    }\n  };\n}).filter(post => post.json.url); // Filtrer les posts sans URL\n\nconsole.log(`✅ ${postsWithScore.length} posts traités avec succès`);\n\n// Trier par score décroissant\nconst sortedPosts = postsWithScore.sort((a, b) => b.json.score - a.json.score);\n\n// Prendre les 10 meilleurs\nconst topPosts = sortedPosts.slice(0, 10);\n\n// Ajouter un rang à chaque post\nconst rankedPosts = topPosts.map((post, index) => ({\n  json: {\n    rank: index + 1,\n    url: post.json.url,\n    caption: post.json.caption,\n    score: post.json.score\n  }\n}));\n\n// Log pour debug\nconsole.log(`🏆 TOP ${topPosts.length} POSTS TIKTOK - Score = (likes×1) + (comments×2) + (shares×3) + (views÷1000)`);\nconsole.log(`────────────────────────────────────────────────────────────────────────────────`);\n\nrankedPosts.forEach((post) => {\n  const p = post.json;\n  const shortCaption = p.caption.length > 80 ? p.caption.substring(0, 80) + '...' : p.caption;\n  console.log(`${p.rank}. Score: ${p.score.toLocaleString()}`);\n  console.log(`   URL: ${p.url}`);\n  console.log(`   Caption: \"${shortCaption}\"`);\n  console.log(`   ────────────────────────────────────────────────────────────────────────────`);\n});\n\nconsole.log(`\\n📊 Analysé ${posts.length} posts TikTok au total`);\nconsole.log(`🎯 ${rankedPosts.length} posts classés et retournés`);\n\n// Retourner les résultats avec le format demandé : url, caption, score\nreturn rankedPosts;"
      },
      "notesInFlow": true,
      "typeVersion": 2
    },
    {
      "id": "61d179e7-c2dc-4476-8e34-a31de02bcd2f",
      "name": "定时触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -704,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {}
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "44b3ea5b-4d30-48e0-a005-155a48d37ec2",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -832,
        -112
      ],
      "parameters": {
        "width": 512,
        "height": 256,
        "content": "# 阶段1:自动化计划激活"
      },
      "typeVersion": 1
    },
    {
      "id": "4b04184d-8bcd-40ca-b93b-be53f02a6a91",
      "name": "便签5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -832,
        192
      ],
      "parameters": {
        "width": 512,
        "height": 304,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "2e6b730b-e0eb-45b0-9087-3162a13ae90a",
      "name": "便签6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        -192
      ],
      "parameters": {
        "color": 5,
        "width": 1440,
        "height": 256,
        "content": "# 阶段2:Reddit 内容发现与处理"
      },
      "typeVersion": 1
    },
    {
      "id": "ea75631f-1c4c-4be4-8dbf-add4d259136c",
      "name": "便签7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        80
      ],
      "parameters": {
        "color": 5,
        "width": 1440,
        "height": 496,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "32e22559-cbba-4942-b7f6-e1b90d7055c2",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        672
      ],
      "parameters": {
        "color": 7,
        "width": 1504,
        "height": 912,
        "content": "# Instagram"
      },
      "typeVersion": 1
    },
    {
      "id": "65294dbf-ad08-4e9a-87b1-11db82848e8a",
      "name": "便签8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        784
      ],
      "parameters": {
        "color": 6,
        "width": 1440,
        "height": 256,
        "content": "# 阶段3:Instagram 内容发现与处理"
      },
      "typeVersion": 1
    },
    {
      "id": "defebed0-d16d-4c16-8e46-79884488ac43",
      "name": "便签9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        1056
      ],
      "parameters": {
        "color": 6,
        "width": 1440,
        "height": 480,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "b69c8d17-84ab-4c38-862a-4ba6ef7ada48",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        1632
      ],
      "parameters": {
        "color": 4,
        "width": 1504,
        "height": 912,
        "content": "# TikTok"
      },
      "typeVersion": 1
    },
    {
      "id": "bc20cc9e-a002-457d-8d3b-f51317058318",
      "name": "便签10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        1712
      ],
      "parameters": {
        "color": 2,
        "width": 1440,
        "height": 256,
        "content": "# 阶段4:TikTok 内容发现与处理"
      },
      "typeVersion": 1
    },
    {
      "id": "af3bf34d-1882-43f8-afd3-16c57b99e1ad",
      "name": "便签11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        2000
      ],
      "parameters": {
        "color": 2,
        "width": 1440,
        "height": 480,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "decd9670-a6a7-4835-ae48-62c53a4eb480",
      "name": "便签12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1648,
        704
      ],
      "parameters": {
        "width": 1040,
        "height": 304,
        "content": "# 阶段5:跨平台数据集成与统一排名"
      },
      "typeVersion": 1
    },
    {
      "id": "df012424-d8b4-46a3-a827-7bd1b52965b2",
      "name": "便签13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1648,
        1056
      ],
      "parameters": {
        "width": 1040,
        "height": 512,
        "content": "# 阶段5:跨平台数据集成与统一排名### 系统功能:"
      },
      "typeVersion": 1
    },
    {
      "id": "656be336-62d4-4b73-8ea6-9202e8b57391",
      "name": "便签14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2736,
        704
      ],
      "parameters": {
        "color": 2,
        "width": 1040,
        "height": 304,
        "content": "# 阶段6:专业分析仪表板生成与交付"
      },
      "typeVersion": 1
    },
    {
      "id": "1aedb57c-4b63-496c-9e39-8e62b1c33e97",
      "name": "便签15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2736,
        1056
      ],
      "parameters": {
        "color": 2,
        "width": 1040,
        "height": 512,
        "content": "### 系统功能:"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "Insta Get": {
      "main": [
        [
          {
            "node": "Sort Instagram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insta Post": {
      "main": [
        [
          {
            "node": "Insta Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insta Wait": {
      "main": [
        [
          {
            "node": "Insta Get",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reddit Get": {
      "main": [
        [
          {
            "node": "Sort Reddit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tiktok Get": {
      "main": [
        [
          {
            "node": "Sort TikTok",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reddit Post": {
      "main": [
        [
          {
            "node": "Reddit Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reddit Wait": {
      "main": [
        [
          {
            "node": "Reddit Get",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sort Reddit": {
      "main": [
        [
          {
            "node": "Insta Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sort TikTok": {
      "main": [
        [
          {
            "node": "Classement global",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "TikTok Wait": {
      "main": [
        [
          {
            "node": "Tiktok Get",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tiktok Post": {
      "main": [
        [
          {
            "node": "TikTok Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sort Instagram": {
      "main": [
        [
          {
            "node": "Tiktok Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Reddit Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classement global": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

高级 - 杂项, 多模态 AI

需要付费吗?

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

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

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

外部链接
在 n8n.io 查看

分享此工作流