8
n8n 中文网amn8n.com

我的工作流程 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)可能需要您自行付费。

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

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

外部链接
在 n8n.io 查看

分享此工作流