我的工作流程 2
高级
这是一个Market Research, AI Summarization领域的自动化工作流,包含 18 个节点。主要使用 Code, GoogleSheets, ScheduleTrigger, ScrapegraphAi 等节点。 AI驱动内容差距分析,使用ScrapeGraphAI和战略规划
前置要求
- •Google Sheets API 凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "VhEwspDqzu7ssFVE",
"meta": {
"instanceId": "f4b0efaa33080e7774e0d9285c40c7abcd2c6f7cf1a8b901fa7106170dd4cda3",
"templateCredsSetupCompleted": true
},
"name": "我的工作流程 2",
"tags": [
{
"id": "DxXGubfBzRKh6L8T",
"name": "Revenue Optimization",
"createdAt": "2025-07-25T16:24:30.370Z",
"updatedAt": "2025-07-25T16:24:30.370Z"
},
{
"id": "IxkcJ2IpYIxivoHV",
"name": "Content Strategy",
"createdAt": "2025-07-25T12:57:37.677Z",
"updatedAt": "2025-07-25T12:57:37.677Z"
},
{
"id": "PAKIJ2Mm9EvRcR3u",
"name": "Trend Monitoring",
"createdAt": "2025-07-25T12:57:37.670Z",
"updatedAt": "2025-07-25T12:57:37.670Z"
},
{
"id": "YtfXmaZk44MYedPO",
"name": "Dynamic Pricing",
"createdAt": "2025-07-25T16:24:30.369Z",
"updatedAt": "2025-07-25T16:24:30.369Z"
},
{
"id": "wJ30mjhtrposO8Qt",
"name": "Simple RAG",
"createdAt": "2025-07-28T12:55:14.424Z",
"updatedAt": "2025-07-28T12:55:14.424Z"
}
],
"nodes": [
{
"id": "41f5943e-f4dd-4ceb-8fbd-ee3dd389dc00",
"name": "Weekly Content Analysis Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1888,
608
],
"parameters": {
"rule": {
"interval": [
{
"field": "weeks"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "b06d07a3-43a3-4dbb-9fdf-d56425993d08",
"name": "AI-Powered Competitor Content Scraper",
"type": "n8n-nodes-scrapegraphai.scrapegraphAi",
"position": [
-1392,
752
],
"parameters": {
"userPrompt": "Analyze all the content on this website and extract comprehensive information about each article. Use this schema: { \"articles\": [{ \"title\": \"Article Title\", \"url\": \"https://competitor.com/article-url\", \"publish_date\": \"2024-01-15\", \"author\": \"Author Name\", \"word_count\": \"1500\", \"main_topic\": \"Primary topic\", \"keywords\": [\"keyword1\", \"keyword2\", \"keyword3\"], \"content_type\": \"blog post\", \"categories\": [\"category1\", \"category2\"], \"meta_description\": \"Article description\", \"headings\": [\"H1 title\", \"H2 subtitle1\", \"H2 subtitle2\"], \"social_shares\": \"150\", \"engagement_metrics\": \"high\" }] }",
"websiteUrl": "https://competitor1.com/blog"
},
"typeVersion": 1
},
{
"id": "cca541bd-729a-482c-a464-1a31449d78ff",
"name": "Secondary Competitor Content Scraper",
"type": "n8n-nodes-scrapegraphai.scrapegraphAi",
"position": [
-1392,
960
],
"parameters": {
"userPrompt": "Extract all content pieces from this competitor website including blog posts, guides, whitepapers, case studies, and resources. Return data using this format: { \"content_pieces\": [{ \"title\": \"Content Title\", \"url\": \"https://example.com/content\", \"content_format\": \"blog post\", \"topic_cluster\": \"Main topic area\", \"target_keywords\": [\"primary keyword\", \"secondary keyword\"], \"difficulty_level\": \"beginner\", \"content_length\": \"2000 words\", \"publication_date\": \"2024-02-01\", \"performance_indicators\": \"high engagement\" }] }",
"websiteUrl": "https://competitor2.com/resources"
},
"typeVersion": 1
},
{
"id": "c2f95dba-233b-4fe5-bbd7-d9718766bcd4",
"name": "Competitor Data Merger and Processor",
"type": "n8n-nodes-base.code",
"notes": "Merges competitor data\nand analyzes content\npatterns and trends",
"position": [
-800,
784
],
"parameters": {
"jsCode": "// Merge data from multiple competitor scrapers\nconst competitor1Data = $input.first().json;\nconst competitor2Data = $input.last().json;\n\n// Extract articles from both competitors\nconst competitor1Articles = competitor1Data.result?.articles || [];\nconst competitor2Content = competitor2Data.result?.content_pieces || [];\n\n// Normalize data structure for consistent processing\nconst normalizeContent = (content, source) => {\n return {\n title: content.title,\n url: content.url,\n publish_date: content.publish_date || content.publication_date,\n author: content.author || 'Unknown',\n word_count: parseInt(content.word_count || content.content_length) || 0,\n main_topic: content.main_topic || content.topic_cluster,\n keywords: content.keywords || content.target_keywords || [],\n content_type: content.content_type || content.content_format,\n categories: content.categories || [content.topic_cluster],\n meta_description: content.meta_description || '',\n headings: content.headings || [],\n social_shares: parseInt(content.social_shares) || 0,\n engagement_metrics: content.engagement_metrics || content.performance_indicators,\n competitor_source: source,\n analyzed_date: new Date().toISOString().split('T')[0]\n };\n};\n\n// Normalize all content\nconst allContent = [\n ...competitor1Articles.map(article => normalizeContent(article, 'competitor1')),\n ...competitor2Content.map(content => normalizeContent(content, 'competitor2'))\n];\n\n// Group content by main topics to identify clusters\nconst topicClusters = {};\nallContent.forEach(content => {\n const topic = content.main_topic?.toLowerCase() || 'uncategorized';\n if (!topicClusters[topic]) {\n topicClusters[topic] = [];\n }\n topicClusters[topic].push(content);\n});\n\n// Analyze keyword frequency across all content\nconst keywordFrequency = {};\nallContent.forEach(content => {\n content.keywords.forEach(keyword => {\n const normalizedKeyword = keyword?.toLowerCase();\n if (normalizedKeyword) {\n keywordFrequency[normalizedKeyword] = (keywordFrequency[normalizedKeyword] || 0) + 1;\n }\n });\n});\n\n// Return processed data for gap analysis\nreturn [{\n json: {\n total_content_analyzed: allContent.length,\n competitor_sources: ['competitor1', 'competitor2'],\n topic_clusters: Object.keys(topicClusters).map(topic => ({\n topic_name: topic,\n content_count: topicClusters[topic].length,\n avg_word_count: Math.round(topicClusters[topic].reduce((sum, content) => sum + content.word_count, 0) / topicClusters[topic].length),\n top_keywords: topicClusters[topic].flatMap(c => c.keywords).slice(0, 10)\n })),\n trending_keywords: Object.entries(keywordFrequency)\n .sort(([,a], [,b]) => b - a)\n .slice(0, 20)\n .map(([keyword, frequency]) => ({ keyword, frequency })),\n content_formats: [...new Set(allContent.map(c => c.content_type))],\n all_content: allContent,\n analysis_summary: {\n most_covered_topics: Object.entries(topicClusters).sort(([,a], [,b]) => b.length - a.length).slice(0, 5).map(([topic, content]) => ({ topic, count: content.length })),\n average_content_length: Math.round(allContent.reduce((sum, content) => sum + content.word_count, 0) / allContent.length),\n content_types_distribution: allContent.reduce((acc, content) => {\n acc[content.content_type] = (acc[content.content_type] || 0) + 1;\n return acc;\n }, {})\n }\n }\n}];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "c83f6b26-eb8a-465d-8baf-4e5e9881a1d4",
"name": "Our Content Library Analyzer",
"type": "n8n-nodes-scrapegraphai.scrapegraphAi",
"position": [
-1392,
544
],
"parameters": {
"userPrompt": "Analyze our existing content library and extract detailed information about each piece. Use this schema: { \"our_content\": [{ \"title\": \"Our Article Title\", \"url\": \"https://yourbrand.com/article\", \"publish_date\": \"2024-01-10\", \"main_topic\": \"Topic area\", \"keywords\": [\"our keyword1\", \"our keyword2\"], \"content_type\": \"blog post\", \"word_count\": \"1200\", \"performance_metrics\": { \"page_views\": \"5000\", \"social_shares\": \"75\", \"engagement_rate\": \"3.2%\" }, \"target_audience\": \"beginners\", \"content_pillars\": [\"pillar1\", \"pillar2\"] }] }",
"websiteUrl": "https://yourbrand.com/blog"
},
"typeVersion": 1
},
{
"id": "25a9cdde-6fd0-4531-a973-f8a77706e0fb",
"name": "Advanced Content Gap Identifier",
"type": "n8n-nodes-base.code",
"notes": "Identifies content gaps\nusing advanced algorithms\nand scoring systems",
"position": [
-176,
704
],
"parameters": {
"jsCode": "// Advanced Content Gap Analysis Algorithm\nconst competitorData = $input.first().json;\nconst ourContentData = $input.last().json;\n\n// Extract our content data\nconst ourContent = ourContentData.result?.our_content || [];\nconst competitorContent = competitorData.all_content || [];\n\n// Create comprehensive topic and keyword maps\nconst ourTopics = new Set(ourContent.map(c => c.main_topic?.toLowerCase()).filter(Boolean));\nconst ourKeywords = new Set(ourContent.flatMap(c => c.keywords || []).map(k => k?.toLowerCase()).filter(Boolean));\n\nconst competitorTopics = new Set(competitorContent.map(c => c.main_topic?.toLowerCase()).filter(Boolean));\nconst competitorKeywords = new Set(competitorContent.flatMap(c => c.keywords || []).map(k => k?.toLowerCase()).filter(Boolean));\n\n// Identify content gaps\nconst topicGaps = [...competitorTopics].filter(topic => !ourTopics.has(topic));\nconst keywordGaps = [...competitorKeywords].filter(keyword => !ourKeywords.has(keyword));\n\n// Analyze competitor content performance patterns\nconst highPerformingContent = competitorContent.filter(content => {\n const socialShares = parseInt(content.social_shares) || 0;\n const wordCount = parseInt(content.word_count) || 0;\n return socialShares > 100 || wordCount > 1500 || content.engagement_metrics === 'high';\n});\n\n// Identify content format gaps\nconst ourFormats = new Set(ourContent.map(c => c.content_type).filter(Boolean));\nconst competitorFormats = new Set(competitorContent.map(c => c.content_type).filter(Boolean));\nconst formatGaps = [...competitorFormats].filter(format => !ourFormats.has(format));\n\n// Calculate content opportunity scores\nconst contentOpportunities = topicGaps.map(topic => {\n const relatedCompetitorContent = competitorContent.filter(c => c.main_topic?.toLowerCase() === topic);\n const avgWordCount = relatedCompetitorContent.reduce((sum, c) => sum + (parseInt(c.word_count) || 0), 0) / relatedCompetitorContent.length || 0;\n const totalSocialShares = relatedCompetitorContent.reduce((sum, c) => sum + (parseInt(c.social_shares) || 0), 0);\n const contentCount = relatedCompetitorContent.length;\n \n // Opportunity scoring algorithm\n let opportunityScore = 0;\n opportunityScore += Math.min(contentCount * 10, 50); // Content volume (max 50 points)\n opportunityScore += Math.min(totalSocialShares / 10, 30); // Social engagement (max 30 points)\n opportunityScore += avgWordCount > 1000 ? 20 : 10; // Content depth (10-20 points)\n \n return {\n topic,\n opportunity_score: Math.round(opportunityScore),\n competitor_content_count: contentCount,\n avg_word_count: Math.round(avgWordCount),\n total_social_engagement: totalSocialShares,\n priority: opportunityScore > 70 ? 'High' : opportunityScore > 40 ? 'Medium' : 'Low',\n suggested_content_types: [...new Set(relatedCompetitorContent.map(c => c.content_type))],\n top_keywords: relatedCompetitorContent.flatMap(c => c.keywords || []).slice(0, 5)\n };\n}).sort((a, b) => b.opportunity_score - a.opportunity_score);\n\n// Generate keyword-specific opportunities\nconst keywordOpportunities = keywordGaps.slice(0, 50).map(keyword => {\n const relatedContent = competitorContent.filter(c => \n c.keywords?.some(k => k?.toLowerCase() === keyword)\n );\n \n return {\n keyword,\n frequency: relatedContent.length,\n avg_word_count: Math.round(relatedContent.reduce((sum, c) => sum + (parseInt(c.word_count) || 0), 0) / relatedContent.length) || 0,\n content_types: [...new Set(relatedContent.map(c => c.content_type))],\n related_topics: [...new Set(relatedContent.map(c => c.main_topic))]\n };\n}).filter(opp => opp.frequency > 0).sort((a, b) => b.frequency - a.frequency);\n\nreturn [{\n json: {\n gap_analysis_summary: {\n total_topic_gaps: topicGaps.length,\n total_keyword_gaps: keywordGaps.length,\n total_format_gaps: formatGaps.length,\n high_priority_opportunities: contentOpportunities.filter(o => o.priority === 'High').length,\n analysis_date: new Date().toISOString().split('T')[0]\n },\n content_opportunities: contentOpportunities.slice(0, 20), // Top 20 opportunities\n keyword_opportunities: keywordOpportunities.slice(0, 30), // Top 30 keyword gaps\n format_gaps: formatGaps,\n competitor_insights: {\n most_successful_topics: competitorData.analysis_summary?.most_covered_topics || [],\n trending_keywords: competitorData.trending_keywords?.slice(0, 15) || [],\n content_formats: competitorData.content_formats || []\n },\n our_content_analysis: {\n total_content_pieces: ourContent.length,\n our_topics: [...ourTopics],\n our_keywords_count: ourKeywords.size,\n our_formats: [...ourFormats]\n },\n recommendations: {\n immediate_actions: contentOpportunities.filter(o => o.priority === 'High').slice(0, 5),\n content_format_expansion: formatGaps.slice(0, 3),\n keyword_targeting: keywordOpportunities.slice(0, 10)\n }\n }\n}];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "a1d27f70-648f-4fb5-a02a-615cb2383837",
"name": "SEO Keyword Mapper and Strategy Builder",
"type": "n8n-nodes-base.code",
"notes": "Maps keywords to content\nopportunities and builds\nSEO content strategies",
"position": [
320,
688
],
"parameters": {
"jsCode": "// Keyword Mapping and SEO Opportunity Analysis\nconst gapData = $input.all()[0].json;\n\n// Extract high-priority content opportunities\nconst contentOpportunities = gapData.content_opportunities || [];\nconst keywordOpportunities = gapData.keyword_opportunities || [];\n\n// Create detailed keyword mapping for each content opportunity\nconst keywordMappedContent = contentOpportunities.map(opportunity => {\n // Find related keyword opportunities\n const relatedKeywords = keywordOpportunities.filter(kwOpp => \n kwOpp.related_topics?.some(topic => topic?.toLowerCase().includes(opportunity.topic))\n );\n \n // Generate primary and secondary keyword recommendations\n const primaryKeywords = relatedKeywords.slice(0, 3).map(k => k.keyword);\n const secondaryKeywords = relatedKeywords.slice(3, 8).map(k => k.keyword);\n const longTailKeywords = relatedKeywords.slice(8, 15).map(k => k.keyword);\n \n // Calculate SEO difficulty and opportunity\n const avgCompetitorFrequency = relatedKeywords.reduce((sum, k) => sum + k.frequency, 0) / relatedKeywords.length || 0;\n const seoOpportunity = avgCompetitorFrequency < 5 ? 'High' : avgCompetitorFrequency < 10 ? 'Medium' : 'Low';\n \n return {\n topic: opportunity.topic,\n opportunity_score: opportunity.opportunity_score,\n priority: opportunity.priority,\n keyword_strategy: {\n primary_keywords: primaryKeywords,\n secondary_keywords: secondaryKeywords,\n long_tail_keywords: longTailKeywords,\n total_keyword_opportunities: relatedKeywords.length,\n seo_difficulty: seoOpportunity\n },\n content_specs: {\n recommended_word_count: opportunity.avg_word_count,\n suggested_formats: opportunity.suggested_content_types,\n competitor_benchmark: opportunity.competitor_content_count\n },\n search_intent_analysis: {\n informational_keywords: relatedKeywords.filter(k => \n k.keyword.includes('how') || k.keyword.includes('what') || k.keyword.includes('why')\n ).map(k => k.keyword),\n commercial_keywords: relatedKeywords.filter(k => \n k.keyword.includes('best') || k.keyword.includes('review') || k.keyword.includes('compare')\n ).map(k => k.keyword),\n transactional_keywords: relatedKeywords.filter(k => \n k.keyword.includes('buy') || k.keyword.includes('price') || k.keyword.includes('discount')\n ).map(k => k.keyword)\n }\n };\n});\n\n// Generate keyword clusters for content planning\nconst keywordClusters = {};\nkeywordOpportunities.forEach(kwOpp => {\n kwOpp.related_topics?.forEach(topic => {\n if (!keywordClusters[topic]) {\n keywordClusters[topic] = [];\n }\n keywordClusters[topic].push({\n keyword: kwOpp.keyword,\n frequency: kwOpp.frequency,\n content_types: kwOpp.content_types\n });\n });\n});\n\n// SEO content recommendations\nconst seoRecommendations = {\n high_opportunity_keywords: keywordOpportunities.filter(k => k.frequency >= 3 && k.frequency <= 8).slice(0, 15),\n content_cluster_opportunities: Object.entries(keywordClusters)\n .filter(([topic, keywords]) => keywords.length >= 3)\n .map(([topic, keywords]) => ({\n cluster_topic: topic,\n pillar_content_opportunity: true,\n supporting_keywords: keywords.slice(0, 10),\n cluster_size: keywords.length\n })),\n competitive_gaps: keywordOpportunities.filter(k => k.frequency === 1).slice(0, 10) // Low competition keywords\n};\n\nreturn [{\n json: {\n keyword_mapped_opportunities: keywordMappedContent,\n keyword_clusters: Object.entries(keywordClusters).map(([topic, keywords]) => ({\n topic,\n keywords: keywords.slice(0, 15),\n cluster_strength: keywords.length\n })),\n seo_recommendations: seoRecommendations,\n content_strategy_insights: {\n pillar_page_opportunities: keywordMappedContent.filter(c => c.keyword_strategy.total_keyword_opportunities > 10).length,\n quick_wins: keywordMappedContent.filter(c => c.keyword_strategy.seo_difficulty === 'High').length,\n competitive_battles: keywordMappedContent.filter(c => c.keyword_strategy.seo_difficulty === 'Low').length\n },\n mapping_summary: {\n total_opportunities_mapped: keywordMappedContent.length,\n total_keywords_analyzed: keywordOpportunities.length,\n clusters_identified: Object.keys(keywordClusters).length,\n mapping_date: new Date().toISOString().split('T')[0]\n }\n }\n}];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "00477a64-0ebd-4c3c-8060-8c256ea6dfd9",
"name": "Strategic Content Planner and Roadmap Generator",
"type": "n8n-nodes-base.code",
"notes": "Creates detailed content\nplans with SEO optimization\nand strategic roadmaps",
"position": [
832,
688
],
"parameters": {
"jsCode": "// Content Planning and Editorial Strategy Generator\nconst keywordData = $input.all()[0].json;\n\n// Extract mapped opportunities\nconst keywordMappedOpportunities = keywordData.keyword_mapped_opportunities || [];\nconst keywordClusters = keywordData.keyword_clusters || [];\nconst seoRecommendations = keywordData.seo_recommendations || {};\n\n// Generate detailed content plans for each opportunity\nconst contentPlans = keywordMappedOpportunities.map((opportunity, index) => {\n // Determine content format based on keywords and competition\n const suggestedFormat = opportunity.content_specs.suggested_formats?.[0] || 'blog post';\n \n // Calculate estimated effort and resources\n const wordCount = opportunity.content_specs.recommended_word_count || 1500;\n const estimatedHours = Math.ceil(wordCount / 250) + 2; // Writing + research + editing\n \n // Generate content outline based on keywords\n const outline = {\n introduction: `Introduction to ${opportunity.topic}`,\n main_sections: opportunity.keyword_strategy.primary_keywords.map(keyword => \n `${keyword.charAt(0).toUpperCase() + keyword.slice(1)}: Comprehensive Guide`\n ),\n supporting_sections: opportunity.keyword_strategy.secondary_keywords.slice(0, 3).map(keyword => \n `Understanding ${keyword}`\n ),\n conclusion: `Key Takeaways and Next Steps for ${opportunity.topic}`\n };\n \n // SEO optimization checklist\n const seoChecklist = {\n primary_keyword_density: '1-2%',\n meta_title: `${opportunity.keyword_strategy.primary_keywords[0]} - [Brand Name]`,\n meta_description: `Learn about ${opportunity.topic}. Comprehensive guide covering ${opportunity.keyword_strategy.primary_keywords.join(', ')}.`,\n header_structure: 'H1 (primary keyword) + H2s (secondary keywords) + H3s (long-tail)',\n internal_linking: 'Link to related existing content',\n external_linking: '2-3 authoritative sources',\n image_optimization: 'Alt text with target keywords'\n };\n \n return {\n content_id: `content-plan-${index + 1}`,\n title: `${opportunity.topic.charAt(0).toUpperCase() + opportunity.topic.slice(1)}: Complete Guide`,\n topic: opportunity.topic,\n priority: opportunity.priority,\n opportunity_score: opportunity.opportunity_score,\n content_format: suggestedFormat,\n target_keywords: {\n primary: opportunity.keyword_strategy.primary_keywords,\n secondary: opportunity.keyword_strategy.secondary_keywords,\n long_tail: opportunity.keyword_strategy.long_tail_keywords\n },\n content_specifications: {\n target_word_count: wordCount,\n estimated_hours: estimatedHours,\n difficulty_level: opportunity.keyword_strategy.seo_difficulty === 'High' ? 'Medium' : 'High',\n content_type: suggestedFormat,\n target_audience: 'Primary target demographic'\n },\n content_outline: outline,\n seo_optimization: seoChecklist,\n search_intent: {\n primary_intent: opportunity.search_intent_analysis.informational_keywords.length > 0 ? 'Informational' : \n opportunity.search_intent_analysis.commercial_keywords.length > 0 ? 'Commercial' : 'Transactional',\n secondary_intents: [\n ...(opportunity.search_intent_analysis.informational_keywords.length > 0 ? ['Informational'] : []),\n ...(opportunity.search_intent_analysis.commercial_keywords.length > 0 ? ['Commercial'] : []),\n ...(opportunity.search_intent_analysis.transactional_keywords.length > 0 ? ['Transactional'] : [])\n ]\n },\n competitive_analysis: {\n competitor_content_count: opportunity.content_specs.competitor_benchmark,\n differentiation_strategy: 'Create more comprehensive, actionable content',\n unique_angle: `Focus on practical implementation and real-world examples`\n },\n success_metrics: {\n primary_kpis: ['Organic traffic growth', 'Keyword ranking improvements', 'Social shares'],\n target_rankings: 'Top 10 for primary keywords within 6 months',\n engagement_goals: 'Above industry average time on page and bounce rate'\n }\n };\n});\n\n// Generate content calendar recommendations\nconst calendarRecommendations = {\n immediate_priority: contentPlans.filter(plan => plan.priority === 'High').slice(0, 5),\n medium_term: contentPlans.filter(plan => plan.priority === 'Medium').slice(0, 8),\n long_term: contentPlans.filter(plan => plan.priority === 'Low').slice(0, 10),\n pillar_content: contentPlans.filter(plan => plan.target_keywords.primary.length > 2).slice(0, 3),\n quick_wins: contentPlans.filter(plan => plan.content_specifications.difficulty_level === 'Medium').slice(0, 5)\n};\n\n// Resource planning\nconst resourcePlanning = {\n total_content_pieces: contentPlans.length,\n estimated_total_hours: contentPlans.reduce((sum, plan) => sum + plan.content_specifications.estimated_hours, 0),\n monthly_capacity: Math.ceil(contentPlans.length / 6), // Spread over 6 months\n content_formats_needed: [...new Set(contentPlans.map(plan => plan.content_format))],\n expertise_required: [...new Set(contentPlans.map(plan => plan.topic))]\n};\n\nreturn [{\n json: {\n content_plans: contentPlans,\n calendar_recommendations: calendarRecommendations,\n resource_planning: resourcePlanning,\n strategic_insights: {\n content_cluster_opportunities: keywordClusters.filter(cluster => cluster.cluster_strength > 5).length,\n seo_quick_wins: seoRecommendations.competitive_gaps?.length || 0,\n pillar_page_potential: keywordClusters.filter(cluster => cluster.cluster_strength > 10).length\n },\n execution_roadmap: {\n month_1: calendarRecommendations.immediate_priority.slice(0, 2),\n month_2: calendarRecommendations.immediate_priority.slice(2, 5),\n month_3: calendarRecommendations.medium_term.slice(0, 3),\n months_4_6: calendarRecommendations.medium_term.slice(3, 8)\n },\n planning_summary: {\n total_opportunities: contentPlans.length,\n high_priority_count: calendarRecommendations.immediate_priority.length,\n estimated_completion_timeline: '6 months',\n planning_date: new Date().toISOString().split('T')[0]\n }\n }\n}];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "c085935b-5499-4624-aaed-165975b68c00",
"name": "Google Sheets Editorial Calendar",
"type": "n8n-nodes-base.googleSheets",
"position": [
2128,
656
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "content_id",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "content_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "title",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "topic",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "topic",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "priority",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "priority",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "opportunity_score",
"type": "number",
"display": true,
"removed": false,
"required": false,
"displayName": "opportunity_score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "content_format",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "content_format",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "target_word_count",
"type": "number",
"display": true,
"removed": false,
"required": false,
"displayName": "target_word_count",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "estimated_hours",
"type": "number",
"display": true,
"removed": false,
"required": false,
"displayName": "estimated_hours",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "primary_keywords",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "primary_keywords",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "secondary_keywords",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "secondary_keywords",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "publication_date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "publication_date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "research_start",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "research_start",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "writing_start",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "writing_start",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "review_date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "review_date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "seo_difficulty",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "seo_difficulty",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "target_audience",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "target_audience",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "content_outline",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "content_outline",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "success_metrics",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "success_metrics",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "status",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "assigned_writer",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "assigned_writer",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "progress",
"type": "number",
"display": true,
"removed": false,
"required": false,
"displayName": "progress",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "notes",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "notes",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": []
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "Content_Gap_Analysis"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": ""
}
},
"typeVersion": 4.5
},
{
"id": "e0866646-e484-4478-a753-3d02364ba9aa",
"name": "Editorial Calendar Generator with Timeline",
"type": "n8n-nodes-base.code",
"notes": "Generates detailed\neditorial calendar with\nproduction timelines",
"position": [
1472,
672
],
"parameters": {
"jsCode": "// Editorial Calendar Generator with Timeline Planning\nconst contentData = $input.all()[0].json;\n\n// Extract content plans and recommendations\nconst contentPlans = contentData.content_plans || [];\nconst executionRoadmap = contentData.execution_roadmap || {};\nconst resourcePlanning = contentData.resource_planning || {};\n\n// Generate monthly editorial calendar\nconst generateEditorialCalendar = () => {\n const calendar = {};\n const currentDate = new Date();\n \n // Create 6-month calendar structure\n for (let i = 0; i < 6; i++) {\n const monthDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + i, 1);\n const monthKey = monthDate.toISOString().slice(0, 7); // YYYY-MM format\n const monthName = monthDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });\n \n calendar[monthKey] = {\n month_name: monthName,\n content_pieces: [],\n total_hours: 0,\n content_types: new Set(),\n priority_distribution: { High: 0, Medium: 0, Low: 0 }\n };\n }\n \n // Distribute content across months based on priority and roadmap\n const distributeContent = (content, monthOffset) => {\n const targetDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + monthOffset, 15);\n const monthKey = targetDate.toISOString().slice(0, 7);\n \n if (calendar[monthKey]) {\n // Calculate publication date (mid-month)\n const publicationDate = targetDate.toISOString().split('T')[0];\n \n // Create detailed calendar entry\n const calendarEntry = {\n content_id: content.content_id,\n title: content.title,\n topic: content.topic,\n priority: content.priority,\n content_format: content.content_format,\n estimated_hours: content.content_specifications.estimated_hours,\n target_word_count: content.content_specifications.target_word_count,\n primary_keywords: content.target_keywords.primary.join(', '),\n secondary_keywords: content.target_keywords.secondary.join(', '),\n publication_date: publicationDate,\n seo_difficulty: content.content_specifications.difficulty_level,\n opportunity_score: content.opportunity_score,\n \n // Production timeline\n research_start: new Date(targetDate.getTime() - 14 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],\n writing_start: new Date(targetDate.getTime() - 10 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],\n review_date: new Date(targetDate.getTime() - 3 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],\n \n // Content specifications\n target_audience: content.search_intent.primary_intent,\n content_outline: Object.values(content.content_outline).join(' | '),\n success_metrics: content.success_metrics.primary_kpis.join(', '),\n \n // Status tracking\n status: 'Planned',\n assigned_writer: 'TBD',\n progress: 0,\n notes: `Gap analysis opportunity - compete with ${content.competitive_analysis.competitor_content_count} competitor pieces`\n };\n \n calendar[monthKey].content_pieces.push(calendarEntry);\n calendar[monthKey].total_hours += content.content_specifications.estimated_hours;\n calendar[monthKey].content_types.add(content.content_format);\n calendar[monthKey].priority_distribution[content.priority]++;\n }\n };\n \n // Distribute content according to roadmap\n (executionRoadmap.month_1 || []).forEach(content => distributeContent(content, 0));\n (executionRoadmap.month_2 || []).forEach(content => distributeContent(content, 1));\n (executionRoadmap.month_3 || []).forEach(content => distributeContent(content, 2));\n (executionRoadmap.months_4_6 || []).forEach((content, index) => {\n distributeContent(content, 3 + (index % 3));\n });\n \n // Convert Set to Array for content_types\n Object.keys(calendar).forEach(monthKey => {\n calendar[monthKey].content_types = [...calendar[monthKey].content_types];\n });\n \n return calendar;\n};\n\nconst editorialCalendar = generateEditorialCalendar();\n\n// Generate team workload analysis\nconst workloadAnalysis = Object.entries(editorialCalendar).map(([monthKey, monthData]) => ({\n month: monthData.month_name,\n total_content_pieces: monthData.content_pieces.length,\n total_hours: monthData.total_hours,\n avg_hours_per_piece: monthData.content_pieces.length > 0 ? Math.round(monthData.total_hours / monthData.content_pieces.length) : 0,\n content_formats: monthData.content_types,\n priority_breakdown: monthData.priority_distribution,\n capacity_status: monthData.total_hours > 160 ? 'Over Capacity' : monthData.total_hours > 120 ? 'High Load' : 'Manageable'\n}));\n\n// Create flat array for Google Sheets export\nconst calendarEntries = Object.values(editorialCalendar).flatMap(month => month.content_pieces);\n\n// Generate editorial insights and recommendations\nconst editorialInsights = {\n total_content_planned: calendarEntries.length,\n total_estimated_hours: calendarEntries.reduce((sum, entry) => sum + entry.estimated_hours, 0),\n content_format_distribution: calendarEntries.reduce((acc, entry) => {\n acc[entry.content_format] = (acc[entry.content_format] || 0) + 1;\n return acc;\n }, {}),\n priority_distribution: calendarEntries.reduce((acc, entry) => {\n acc[entry.priority] = (acc[entry.priority] || 0) + 1;\n return acc;\n }, {}),\n avg_opportunity_score: Math.round(calendarEntries.reduce((sum, entry) => sum + entry.opportunity_score, 0) / calendarEntries.length),\n peak_production_months: workloadAnalysis.filter(month => month.capacity_status !== 'Manageable').map(month => month.month)\n};\n\nreturn calendarEntries.map(entry => ({ json: entry }));"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "51f1fd73-4a40-4c90-9fbf-c375f2b64597",
"name": "Step 1 - Schedule Trigger Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2144,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 1:每周内容分析触发器 📅"
},
"typeVersion": 1
},
{
"id": "27c218d2-8fe7-490e-8cc6-bec3a99bc75b",
"name": "Step 2 - Competitor Scraper Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1568,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 2:AI 驱动的竞争对手内容采集器 🤖"
},
"typeVersion": 1
},
{
"id": "2a428c81-c8dc-409f-a7b8-98d684130c8c",
"name": "Step 3 - Content Library Analyzer Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
-992,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 3:我们的内容库分析器 📚"
},
"typeVersion": 1
},
{
"id": "6aaa72ce-810c-48d7-b86d-2475a67d9940",
"name": "Step 4 - Gap Identifier Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
-416,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 4:高级内容差距识别器 🎯"
},
"typeVersion": 1
},
{
"id": "9f186b0b-18fa-4389-9c23-70c0ccea56bc",
"name": "Step 5 - Keyword Mapper Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
160,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 5:SEO 关键词映射与策略构建器 🔍"
},
"typeVersion": 1
},
{
"id": "c8f5d487-bf1d-4123-bf30-5edaccdb99af",
"name": "Step 6 - Content Planner Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
736,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 6:战略内容规划师与路线图生成器 📋"
},
"typeVersion": 1
},
{
"id": "ac94f7f0-1736-4a3b-a760-0fd647b489d4",
"name": "Step 7 - Editorial Calendar Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
1312,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 7:带时间线的编辑日历生成器 📆"
},
"typeVersion": 1
},
{
"id": "e49ef381-9da4-4712-81b5-0867bc5b0b1b",
"name": "Step 8 - Sheets Storage Info",
"type": "n8n-nodes-base.stickyNote",
"position": [
1888,
-240
],
"parameters": {
"color": 6,
"width": 575,
"height": 1474,
"content": "# 步骤 8:Google Sheets 编辑日历存储 📊"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "4ed2a770-33de-4fe0-b31b-de6d8ca9f073",
"connections": {
"Our Content Library Analyzer": {
"main": [
[
{
"node": "Advanced Content Gap Identifier",
"type": "main",
"index": 0
}
]
]
},
"Advanced Content Gap Identifier": {
"main": [
[
{
"node": "SEO Keyword Mapper and Strategy Builder",
"type": "main",
"index": 0
}
]
]
},
"Weekly Content Analysis Trigger": {
"main": [
[
{
"node": "AI-Powered Competitor Content Scraper",
"type": "main",
"index": 0
},
{
"node": "Secondary Competitor Content Scraper",
"type": "main",
"index": 0
},
{
"node": "Our Content Library Analyzer",
"type": "main",
"index": 0
}
]
]
},
"Competitor Data Merger and Processor": {
"main": [
[
{
"node": "Advanced Content Gap Identifier",
"type": "main",
"index": 0
}
]
]
},
"Secondary Competitor Content Scraper": {
"main": [
[
{
"node": "Competitor Data Merger and Processor",
"type": "main",
"index": 0
}
]
]
},
"AI-Powered Competitor Content Scraper": {
"main": [
[
{
"node": "Competitor Data Merger and Processor",
"type": "main",
"index": 0
}
]
]
},
"SEO Keyword Mapper and Strategy Builder": {
"main": [
[
{
"node": "Strategic Content Planner and Roadmap Generator",
"type": "main",
"index": 0
}
]
]
},
"Editorial Calendar Generator with Timeline": {
"main": [
[
{
"node": "Google Sheets Editorial Calendar",
"type": "main",
"index": 0
}
]
]
},
"Strategic Content Planner and Roadmap Generator": {
"main": [
[
{
"node": "Editorial Calendar Generator with Timeline",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 市场调研, AI 摘要总结
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
使用 ScrapegraphAI 自动抓取新闻文章并存储到 Google Sheets
使用ScrapegraphAI自动抓取新闻文章并存储到Google Sheets
Code
Google Sheets
Schedule Trigger
+2
8 节点vinci-king-01
市场调研
我的工作流 2
结合 AI 竞品监控和收入优化的自动化动态定价
If
Code
Merge
+8
25 节点vinci-king-01
市场调研
我的工作流2
使用ScrapeGraphAI和Google Sheets发现和分析SEO反向链接
Code
Filter
Email Send
+4
17 节点vinci-king-01
市场调研
我的工作流程2
使用AI、Slack和Google Sheets监控社交媒体内容趋势
Code
Merge
Slack
+5
17 节点vinci-king-01
市场调研
竞品价格监控
带AI组件和警报的价格监控仪表板
If
Code
Slack
+6
15 节点vinci-king-01
市场调研
Amazon 键盘产品智能抓取与 Google Sheets 集成
使用 ScrapeGraphAI 抓取 Amazon 键盘产品到 Google Sheets
Code
Google Sheets
Schedule Trigger
+2
8 节点vinci-king-01
市场调研