使用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)可能需要您自行付费。
相关工作流推荐
Google广告活动报告自动生成到Google表格(含Airtable客户管理)
Google广告活动报告自动生成到Google表格(含Airtable客户管理)
If
Code
Wait
+6
31 节点Growth AI
杂项
使用 HTTP Last-Modified 检查从 Google Sheets 获取职位发布过期和刷新提醒
通过 Google Sheets、HTTP 检查和 Gmail 实现职位发布过期提醒的自动化
If
Set
Code
+6
19 节点WeblineIndia
人力资源
使用Motion和Airtable自动编辑任务跟踪与通知
使用Motion和Airtable自动编辑任务跟踪与通知
If
Code
Gmail
+6
12 节点Growth AI
项目管理
使用 BOAMP API 和 Google Sheets 监控并筛选法国采购招标
使用 BOAMP API 和 Google Sheets 监控并筛选法国采购招标
If
Code
Wait
+7
55 节点Growth AI
内容创作
自动化新闻监控与Claude 4 AI分析,用于Discord和Google新闻
自动化新闻监控与Claude 4 AI分析,用于Discord和Google新闻
Code
Discord
Aggregate
+8
30 节点Growth AI
杂项
08 - AI视频生成
使用OpenAI、Veo 3和Gmail为任何主题创建短视频
If
Wait
Gmail
+4
14 节点Avkash Kakdiya
内容创作