Gemini AIを使ってGoogle Mapsのレビューから市場調査レポートを生成

中級

これはMarket Research, AI Summarization分野の自動化ワークフローで、15個のノードを含みます。主にSet, Code, Gmail, Aggregate, HttpRequestなどのノードを使用。 Google MapsのレビューからGemini AIで市場リサーチレポートを生成

前提条件
  • Googleアカウント + Gmail API認証情報
  • ターゲットAPIの認証情報が必要な場合あり
  • Google Gemini API Key

カテゴリー

ワークフロープレビュー
ノード接続関係を可視化、ズームとパンをサポート
ワークフローをエクスポート
以下のJSON設定をn8nにインポートして、このワークフローを使用できます
{
  "meta": {
    "instanceId": "fec3a82e5888606db7f18bce6d6a86acc72be4b93a270dd22cfc357c585bfc28"
  },
  "nodes": [
    {
      "id": "7a561138-c149-4f92-97b7-087771017521",
      "name": "ワークフロー開始",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -608,
        288
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "acc1a472-587e-4dc2-b215-e986ca907163",
      "name": "ユーザー入力設定",
      "type": "n8n-nodes-base.set",
      "position": [
        -416,
        288
      ],
      "parameters": {
        "fields": {
          "values": [
            {
              "name": "search_query",
              "stringValue": "nhà hàng quận 1 TP.HCM"
            },
            {
              "name": "search_location",
              "stringValue": "@10.8231,106.6297,12z"
            },
            {
              "name": "language_code",
              "stringValue": "vi"
            },
            {
              "name": "analysis_focus",
              "stringValue": "restaurant"
            },
            {
              "name": "city_name",
              "stringValue": "TP. Hồ Chí Minh"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "9c3ced66-5ec2-4e70-8150-e11711bc3f90",
      "name": "動的場所検索",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -208,
        288
      ],
      "parameters": {
        "url": "https://serpapi.com/search.json",
        "options": {},
        "jsonQuery": "={\n  \"engine\": \"google_maps\",\n  \"q\": \"{{ $('User Input Configuration').item.json.search_query }}\",\n  \"ll\": \"{{ $('User Input Configuration').item.json.search_location }}\",\n  \"hl\": \"{{ $('User Input Configuration').item.json.language_code }}\",\n  \"api_key\": \"ffbea3b40ea41b0e10dbceb1302b81209669f83c5b9b75ed8309dbc3f2ae624d\"\n}",
        "sendQuery": true,
        "specifyQuery": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "0062335c-2c19-4132-938c-37a7013668ed",
      "name": "拡張データ抽出",
      "type": "n8n-nodes-base.code",
      "position": [
        -16,
        288
      ],
      "parameters": {
        "jsCode": "// Enhanced extraction with better filtering and validation\nconst localResults = $input.first().json.local_results || [];\nconst userConfig = $('User Input Configuration').item.json;\n\nconsole.log(`Processing ${localResults.length} search results for: ${userConfig.search_query}`);\n\n// Extract and filter places with more comprehensive data\nconst places = localResults.map(place => {\n  // Extract additional data that might be useful for analysis\n  const placeData = {\n    place_name: place.title || 'Unknown',\n    address: place.address || 'No address',\n    rating: place.rating || 'No rating',\n    reviews_count: place.reviews || 0,\n    reviews_link: place.reviews_link || null,\n    data_id: place.data_id || null,\n    type: place.type || 'Unknown',\n    place_id: place.place_id || null,\n    lsig: place.lsig || null,\n    phone: place.phone || null,\n    website: place.website || null,\n    thumbnail: place.thumbnail || null,\n    operating_hours: place.operating_hours || null,\n    price_level: place.price_level || null,\n    plus_code: place.plus_code || null,\n    coordinates: {\n      lat: place.gps_coordinates?.latitude || null,\n      lng: place.gps_coordinates?.longitude || null\n    },\n    // Add context about what we're analyzing\n    search_context: {\n      original_query: userConfig.search_query,\n      analysis_focus: userConfig.analysis_focus,\n      city: userConfig.city_name,\n      search_timestamp: new Date().toISOString()\n    }\n  };\n  \n  return placeData;\n}).filter(place => {\n  // Enhanced filtering - keep places that have either reviews_link OR sufficient basic data\n  return place.reviews_link || (place.rating && place.rating !== 'No rating') || place.reviews_count > 0;\n});\n\nconsole.log(`Filtered to ${places.length} places with review data or sufficient information`);\nconsole.log(`Analysis focus: ${userConfig.analysis_focus} in ${userConfig.city_name}`);\n\n// Return array of places with enhanced metadata\nreturn places.map(place => ({ json: place }));"
      },
      "typeVersion": 2
    },
    {
      "id": "136b7156-e38c-43a2-95d5-38a76c8e067a",
      "name": "場所ごとのループ処理",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        192,
        80
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "31112897-af1c-4476-85b9-81a7f287a7ca",
      "name": "レビュー内容取得",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        240
      ],
      "parameters": {
        "url": "={{ $json.reviews_link }}",
        "options": {
          "timeout": 30000,
          "allowUnauthorizedCerts": true
        },
        "jsonQuery": "{\n  \"api_key\": \"ffbea3b40ea41b0e10dbceb1302b81209669f83c5b9b75ed8309dbc3f2ae624d\"\n}",
        "sendQuery": true,
        "specifyQuery": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "c9f7f981-8476-4ba1-8448-5f29ae13d989",
      "name": "拡張データ結合",
      "type": "n8n-nodes-base.code",
      "position": [
        592,
        384
      ],
      "parameters": {
        "jsCode": "// Enhanced data combination with error handling\nconst currentItem = $('Loop Over Places').item;\nconst reviewsResponse = $input.first().json;\nconst userConfig = $('User Input Configuration').item.json;\n\n// Enhanced combined data with more comprehensive information\nconst combinedData = {\n  // Basic place information\n  place_name: currentItem.json.place_name,\n  address: currentItem.json.address,\n  rating: currentItem.json.rating,\n  reviews_count: currentItem.json.reviews_count,\n  data_id: currentItem.json.data_id,\n  type: currentItem.json.type,\n  phone: currentItem.json.phone,\n  website: currentItem.json.website,\n  coordinates: currentItem.json.coordinates,\n  operating_hours: currentItem.json.operating_hours,\n  price_level: currentItem.json.price_level,\n  \n  // Reviews content with error handling\n  reviews_content: reviewsResponse || { error: 'No reviews data available' },\n  \n  // Enhanced metadata for analysis\n  analysis_metadata: {\n    search_query: userConfig.search_query,\n    analysis_focus: userConfig.analysis_focus,\n    target_city: userConfig.city_name,\n    data_collection_time: new Date().toISOString(),\n    has_reviews: !!(reviewsResponse && reviewsResponse.reviews),\n    review_source: currentItem.json.reviews_link || 'No review link',\n    place_completeness_score: calculateCompletenessScore(currentItem.json)\n  }\n};\n\n// Function to calculate how complete the place data is\nfunction calculateCompletenessScore(placeData) {\n  const fields = ['place_name', 'address', 'rating', 'phone', 'website', 'operating_hours'];\n  const filledFields = fields.filter(field => \n    placeData[field] && placeData[field] !== 'Unknown' && placeData[field] !== 'No address'\n  );\n  return Math.round((filledFields.length / fields.length) * 100);\n}\n\nconsole.log(`Processing ${combinedData.analysis_metadata.analysis_focus}: ${combinedData.place_name}`);\nconsole.log(`Data completeness: ${combinedData.analysis_metadata.place_completeness_score}%`);\n\nreturn { json: combinedData };"
      },
      "typeVersion": 2
    },
    {
      "id": "68e789cd-2df5-4783-8891-5cd4d6eb0553",
      "name": "全データ収集",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        800,
        288
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "d1084cb2-0d34-4f6a-8a51-f87b1c01c6c3",
      "name": "分析データ準備",
      "type": "n8n-nodes-base.code",
      "position": [
        992,
        288
      ],
      "parameters": {
        "jsCode": "// Enhanced data preparation with flexible analysis structure\nconst allPlaces = $input.all();\nconst userConfig = $('User Input Configuration').item.json;\n\nconsole.log(`Preparing analysis for ${allPlaces.length} ${userConfig.analysis_focus} locations in ${userConfig.city_name}`);\n\n// Create comprehensive analysis dataset\nlet analysisPrompt = `MARKET RESEARCH DATA ANALYSIS\\n`;\nanalysisPrompt += `Research Focus: ${userConfig.analysis_focus}\\n`;\nanalysisPrompt += `Location: ${userConfig.city_name}\\n`;\nanalysisPrompt += `Search Query: \"${userConfig.search_query}\"\\n`;\nanalysisPrompt += `Total Locations Found: ${allPlaces.length}\\n`;\nanalysisPrompt += `Analysis Date: ${new Date().toLocaleDateString('vi-VN')}\\n\\n`;\n\n// Enhanced data summary\nanalysisPrompt += `=== DATASET OVERVIEW ===\\n`;\nlet totalReviews = 0;\nlet avgRating = 0;\nlet placesWithReviews = 0;\nlet completenessScores = [];\n\nallPlaces.forEach((place, index) => {\n  const data = place.json;\n  \n  // Calculate statistics\n  if (data.reviews_count && typeof data.reviews_count === 'number') {\n    totalReviews += data.reviews_count;\n  }\n  \n  if (data.rating && data.rating !== 'No rating') {\n    avgRating += parseFloat(data.rating) || 0;\n  }\n  \n  if (data.reviews_content && data.reviews_content.reviews) {\n    placesWithReviews++;\n  }\n  \n  if (data.analysis_metadata && data.analysis_metadata.place_completeness_score) {\n    completenessScores.push(data.analysis_metadata.place_completeness_score);\n  }\n  \n  // Add detailed place information\n  analysisPrompt += `\\n${index + 1}. ${data.place_name}\\n`;\n  analysisPrompt += `   Address: ${data.address}\\n`;\n  analysisPrompt += `   Rating: ${data.rating}/5 (${data.reviews_count || 0} reviews)\\n`;\n  \n  // Add contact and operational info if available\n  if (data.phone) analysisPrompt += `   Phone: ${data.phone}\\n`;\n  if (data.website) analysisPrompt += `   Website: ${data.website}\\n`;\n  if (data.operating_hours) analysisPrompt += `   Hours: ${JSON.stringify(data.operating_hours)}\\n`;\n  if (data.price_level) analysisPrompt += `   Price Level: ${data.price_level}\\n`;\n  \n  // Add review samples if available\n  if (data.reviews_content && data.reviews_content.reviews) {\n    analysisPrompt += `   Recent Reviews:\\n`;\n    data.reviews_content.reviews.slice(0, 5).forEach(review => {\n      const reviewText = review.snippet || review.text || review.comment || 'No text available';\n      const reviewRating = review.rating || 'No rating';\n      analysisPrompt += `   - \"${reviewText.substring(0, 200)}...\" (${reviewRating}/5)\\n`;\n    });\n  }\n  \n  analysisPrompt += `   Data Completeness: ${data.analysis_metadata?.place_completeness_score || 'N/A'}%\\n`;\n  analysisPrompt += `\\n${'-'.repeat(60)}\\n`;\n});\n\n// Add summary statistics\nconst avgRatingCalculated = allPlaces.length > 0 ? (avgRating / allPlaces.length).toFixed(2) : 0;\nconst avgCompleteness = completenessScores.length > 0 ? \n  (completenessScores.reduce((a, b) => a + b, 0) / completenessScores.length).toFixed(1) : 'N/A';\n\nanalysisPrompt += `\\n=== SUMMARY STATISTICS ===\\n`;\nanalysisPrompt += `Total Places Analyzed: ${allPlaces.length}\\n`;\nanalysisPrompt += `Places with Review Data: ${placesWithReviews}\\n`;\nanalysisPrompt += `Total Reviews Collected: ${totalReviews}\\n`;\nanalysisPrompt += `Average Rating: ${avgRatingCalculated}/5\\n`;\nanalysisPrompt += `Average Data Completeness: ${avgCompleteness}%\\n`;\n\nreturn {\n  json: {\n    analysis_prompt: analysisPrompt,\n    raw_data: allPlaces.map(place => place.json),\n    summary_stats: {\n      total_places: allPlaces.length,\n      places_with_reviews: placesWithReviews,\n      total_reviews: totalReviews,\n      average_rating: avgRatingCalculated,\n      average_completeness: avgCompleteness,\n      search_context: userConfig\n    }\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "0cdd33f4-447d-4246-98bf-410e562686c1",
      "name": "AI市場分析",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1168,
        288
      ],
      "parameters": {
        "text": "=Bạn là chuyên gia phân tích và nghiên cứu thị trường chuyên sâu với khả năng tạo ra các báo cáo insights chi tiết và actionable.\n\nBạn đã nhận được dữ liệu thị trường thực tế về \"{{ $('User Input Configuration').item.json.analysis_focus }}\" tại {{ $('User Input Configuration').item.json.city_name }} với đầy đủ thông tin từ khách hàng thực tế.\n\n=== DỮ LIỆU PHÂN TÍCH ===\n{{ JSON.stringify($json.raw_data, null, 2) }}\n\n=== NHIỆM VỤ PHÂN TÍCH ===\n\nHãy tạo một báo cáo phân tích thị trường toàn diện với cấu trúc sau:\n\n**I. TÓM TẮT ĐIỀU HÀNH (EXECUTIVE SUMMARY)**\n- Insights chính trong 3-4 câu\n- Cơ hội kinh doanh lớn nhất\n- Khuyến nghị hành động ngay lập tức\n\n**II. PHÂN TÍCH TỔNG QUAN THỊ TRƯỜNG**\n- Quy mô và mật độ thị trường {{ $('User Input Configuration').item.json.analysis_focus }} tại {{ $('User Input Configuration').item.json.city_name }}\n- Phân khúc giá và chất lượng dịch vụ\n- Phân bố địa lý và khu vực hot\n- Mức độ cạnh tranh và gap thị trường\n\n**III. PHÂN TÍCH TRẢI NGHIỆM KHÁCH HÀNG**\n- Top 5 yếu tố được đánh giá cao nhất (với % khách hàng mention)\n- Top 5 vấn đề phổ biến nhất (với tần suất xuất hiện)\n- Sentiment analysis tổng thể\n- Customer journey và pain points chính\n\n**IV. COMPETITIVE LANDSCAPE**\n- Phân tích 3-5 player mạnh nhất\n- Điểm mạnh/yếu của từng competitor\n- Pricing strategy và value proposition\n- Market positioning gaps\n\n**V. INSIGHTS VÀ XU HƯỚNG**\n- Behavioral patterns của target customers\n- Emerging trends và changing preferences\n- Seasonal/temporal patterns nếu có\n- Technology adoption và digital readiness\n\n**VI. KHUYẾN NGHỊ CHIẾN LƯỢC**\n- Business model tối ưu cho thị trường này\n- Pricing strategy và revenue streams\n- Marketing approach và customer acquisition\n- Operational excellence priorities\n- Investment areas và resource allocation\n\n**VII. ACTION PLAN**\n- 3 hành động ngay lập tức (next 30 days)\n- 3 initiatives trung hạn (3-6 months)\n- Long-term strategy (6-12 months)\n\nYêu cầu:\n- Sử dụng số liệu cụ thể từ dữ liệu thực tế\n- Đưa ra insights độc đáo, không chỉ mô tả\n- Tập trung vào actionable recommendations\n- Viết bằng tiếng Việt chuyên nghiệp nhưng dễ hiểu\n- Highlight key metrics và success factors\n- Đề xuất KPIs để theo dõi success",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "82c701cb-a88a-410c-bda4-aaaf4c5227ef",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1152,
        480
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "id": "MT1SUsx0Hy6XtV22",
          "name": "Google Gemini(PaLM) Api account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2ec72fea-962e-45d4-aa48-abf02c0d80de",
      "name": "メール内容準備",
      "type": "n8n-nodes-base.code",
      "position": [
        1536,
        176
      ],
      "parameters": {
        "jsCode": "// Simplified email preparation - Analysis content only\nconst reportContent = $input.first().json.output;\nconst userConfig = $('User Input Configuration').item.json;\nconst summaryStats = $('Prepare Analysis Data').item.json.summary_stats;\n\n// Generate dynamic subject based on analysis focus\nconst analysisType = userConfig.analysis_focus;\nconst cityName = userConfig.city_name;\nconst currentDate = new Date().toLocaleDateString('vi-VN');\n\nconst emailSubject = `📊 Báo Cáo Phân Tích Thị Trường ${analysisType} ${cityName} - ${currentDate}`;\n\n// Enhanced HTML formatting function\nfunction formatToHTML(content) {\n  let html = content\n    // Convert markdown-style formatting\n    .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n    .replace(/\\*([^*]+)\\*/g, '<em>$1</em>')\n    \n    // Convert main sections (I., II., III., etc.)\n    .replace(/\\*\\*([IVX]+\\. [^*]+)\\*\\*/g, '<h2 style=\"color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 8px; margin-top: 30px;\">$1</h2>')\n    \n    // Convert subsections\n    .replace(/\\*\\*([^*]+:)\\*\\*/g, '<h3 style=\"color: #34495e; margin-top: 20px; margin-bottom: 10px;\">$1</h3>')\n    \n    // Convert numbered lists with enhanced styling\n    .replace(/^(\\d+\\. )/gm, '<div style=\"margin: 8px 0;\"><strong style=\"color: #3498db;\">$1</strong>')\n    \n    // Convert bullet points with different levels\n    .replace(/^- /gm, '<div style=\"margin-left: 20px; margin: 5px 0;\">• ')\n    .replace(/^  - /gm, '<div style=\"margin-left: 40px; margin: 5px 0;\">◦ ')\n    .replace(/^    - /gm, '<div style=\"margin-left: 60px; margin: 5px 0;\">▪ ')\n    \n    // Close div tags and handle line breaks\n    .replace(/(• [^<\\n]+)(?=\\n|$)/g, '$1</div>')\n    .replace(/(◦ [^<\\n]+)(?=\\n|$)/g, '$1</div>')\n    .replace(/(▪ [^<\\n]+)(?=\\n|$)/g, '$1</div>')\n    .replace(/\\n\\n/g, '</p><p>')\n    .replace(/\\n/g, '<br>');\n\n  return '<p>' + html + '</p>'.replace(/<p><\\/p>/g, '');\n}\n\n// Create comprehensive HTML email template - Analysis focused\nconst htmlContent = `\n<!DOCTYPE html>\n<html lang=\"vi\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>${emailSubject}</title>\n    <style>\n        body {\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n            line-height: 1.6;\n            color: #333;\n            max-width: 900px;\n            margin: 0 auto;\n            padding: 20px;\n            background-color: #f8f9fa;\n        }\n        .container {\n            background-color: white;\n            padding: 40px;\n            border-radius: 12px;\n            box-shadow: 0 4px 20px rgba(0,0,0,0.1);\n        }\n        .header {\n            text-align: center;\n            margin-bottom: 40px;\n            padding: 30px;\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            color: white;\n            border-radius: 10px;\n        }\n        .header h1 {\n            margin: 0;\n            font-size: 26px;\n            font-weight: bold;\n        }\n        .header .subtitle {\n            font-size: 16px;\n            margin-top: 10px;\n            opacity: 0.95;\n        }\n        .stats-overview {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n            gap: 20px;\n            margin: 30px 0;\n            padding: 25px;\n            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);\n            border-radius: 10px;\n        }\n        .stat-item {\n            text-align: center;\n            padding: 15px;\n            background: white;\n            border-radius: 8px;\n            box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n        }\n        .stat-number {\n            font-size: 24px;\n            font-weight: bold;\n            color: #3498db;\n        }\n        .stat-label {\n            font-size: 12px;\n            color: #7f8c8d;\n            margin-top: 5px;\n        }\n        h2 {\n            color: #2c3e50;\n            border-bottom: 2px solid #3498db;\n            padding-bottom: 8px;\n            margin-top: 30px;\n        }\n        h3 {\n            color: #34495e;\n            margin-top: 20px;\n            margin-bottom: 10px;\n        }\n        .highlight {\n            background: #fff3cd;\n            border: 1px solid #ffeaa7;\n            padding: 15px;\n            border-radius: 5px;\n            margin: 15px 0;\n        }\n        .footer {\n            margin-top: 40px;\n            padding: 25px;\n            background: #34495e;\n            color: white;\n            text-align: center;\n            border-radius: 10px;\n            font-size: 14px;\n        }\n        @media (max-width: 600px) {\n            body { padding: 10px; }\n            .container { padding: 20px; }\n            .stats-overview { grid-template-columns: 1fr; }\n        }\n    </style>\n</head>\n<body>\n    <div class=\"container\">\n        <div class=\"header\">\n            <h1>📊 BÁO CÁO PHÂN TÍCH THỊ TRƯỜNG</h1>\n            <div class=\"subtitle\">${analysisType} tại ${cityName}</div>\n            <div style=\"font-size: 14px; margin-top: 15px; opacity: 0.9;\">\n                Ngày phân tích: ${new Date().toLocaleDateString('vi-VN', { \n                    year: 'numeric', \n                    month: 'long', \n                    day: 'numeric',\n                    hour: '2-digit',\n                    minute: '2-digit'\n                })}\n            </div>\n        </div>\n        \n        ${formatToHTML(reportContent)}\n        \n        <div class=\"footer\">\n            <p><strong>🚀 Market Research Analytics System</strong></p>\n            <p>📧 Báo cáo được tạo tự động từ dữ liệu thực tế \n            <p>🎯 Phân tích: ${analysisType} | 📍 Địa điểm: ${cityName}</p>\n            <p style=\"margin-top: 15px; font-size: 12px; opacity: 0.8;\">\n                Query gốc: \"${userConfig.search_query}\"\n            </p>\n        </div>\n    </div>\n</body>\n</html>\n`;\n\n// Create simplified plain text version - Analysis only\nconst plainTextContent = `\nBÁO CÁO PHÂN TÍCH THỊ TRƯỜNG - ${analysisType.toUpperCase()} ${cityName.toUpperCase()}\n${'='.repeat(60)}\n\nSỐ LIỆU TỔNG QUAN:\n- Địa điểm phân tích: ${summaryStats.total_places}\n- Tổng đánh giá: ${summaryStats.total_reviews}\n- Rating TB: ${summaryStats.average_rating}/5\n- Có data chi tiết: ${summaryStats.places_with_reviews}\n\n${reportContent.replace(/\\*\\*([^*]+)\\*\\*/g, '$1').replace(/\\*/g, '')}\n\n${'='.repeat(60)}\nBáo cáo tự động - ${currentDate}\n`;\n\nreturn {\n    json: {\n        htmlContent: htmlContent,\n        plainTextContent: plainTextContent,\n        subject: emailSubject,\n        analysis_metadata: {\n            analysis_type: analysisType,\n            city: cityName,\n            total_locations: summaryStats.total_places,\n            total_reviews: summaryStats.total_reviews,\n            average_rating: summaryStats.average_rating,\n            search_query: userConfig.search_query,\n            generation_time: new Date().toISOString()\n        }\n    }};"
      },
      "typeVersion": 2
    },
    {
      "id": "7dd14586-d25d-4b12-baef-2be0c6987456",
      "name": "メールレポート送信",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1728,
        384
      ],
      "webhookId": "4632d0f8-2acf-4508-acf5-f2d1e9a77bbf",
      "parameters": {
        "sendTo": "truong11062002@gmail.com",
        "message": "={{ $json.htmlContent }}",
        "options": {
          "ccList": "",
          "bccList": "",
          "replyTo": ""
        },
        "subject": "={{ $json.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "9hqpp5Ew9HbGDqNu",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "1ecc0e85-5ec2-4f9f-a6e8-23a3f7f826a8",
      "name": "最終ステータス&ログ記録",
      "type": "n8n-nodes-base.code",
      "position": [
        1936,
        384
      ],
      "parameters": {
        "jsCode": "// Final workflow status and comprehensive logging\nconst emailResult = $input.first().json;\nconst userConfig = $('User Input Configuration').item.json;\nconst summaryStats = $('Prepare Analysis Data').item.json.summary_stats;\nconst analysisMetadata = $('Prepare Email Content').item.json.analysis_metadata;\n\nconst completionTime = new Date().toISOString();\n\nconsole.log('🎉 ========= FLEXIBLE MARKET ANALYSIS COMPLETED =========');\nconsole.log(`📊 Analysis Type: ${userConfig.analysis_focus}`);\nconsole.log(`🏙️  Target City: ${userConfig.city_name}`);\nconsole.log(`🔍 Search Query: \"${userConfig.search_query}\"`);\nconsole.log(`📍 Total Locations Analyzed: ${summaryStats.total_places}`);\nconsole.log(`💬 Total Reviews Processed: ${summaryStats.total_reviews}`);\nconsole.log(`⭐ Average Rating: ${summaryStats.average_rating}/5`);\nconsole.log(`📧 Email Report Sent Successfully`);\nconsole.log(`⏰ Completion Time: ${completionTime}`);\nconsole.log('=========================================================');\n\n// Generate comprehensive final report\nconst workflowSummary = {\n  workflow_status: 'COMPLETED_SUCCESSFULLY',\n  workflow_type: 'FLEXIBLE_MARKET_RESEARCH_ANALYSIS',\n  execution_metadata: {\n    analysis_focus: userConfig.analysis_focus,\n    target_location: userConfig.city_name,\n    search_query: userConfig.search_query,\n    language_code: userConfig.language_code,\n    completion_time: completionTime\n  },\n  data_collection_results: {\n    total_places_found: summaryStats.total_places,\n    places_with_review_data: summaryStats.places_with_reviews,\n    total_reviews_collected: summaryStats.total_reviews,\n    average_rating_calculated: summaryStats.average_rating,\n    data_completeness_average: summaryStats.average_completeness\n  },\n  analysis_outputs: {\n    ai_model_used: 'Google_Gemini',\n    report_format: 'Comprehensive_Market_Analysis',\n    email_delivery_status: emailResult.messageId ? 'SUCCESS' : 'UNKNOWN',\n    email_subject: analysisMetadata.subject,\n    email_recipient: 'truong11062002@gmail.com'\n  },\n  workflow_capabilities: {\n    flexible_search_query: true,\n    dynamic_location_targeting: true,\n    multi_language_support: true,\n    comprehensive_data_extraction: true,\n    ai_powered_analysis: true,\n    automated_report_generation: true,\n    email_delivery: true\n  },\n  next_steps_recommendations: [\n    'Review the detailed market analysis report in your email',\n    'Customize search parameters for different market segments',\n    'Set up recurring analysis for market monitoring',\n    'Expand analysis to additional cities or business categories',\n    'Integrate insights into business strategy planning'\n  ]\n};\n\nreturn { json: workflowSummary };"
      },
      "typeVersion": 2
    },
    {
      "id": "599485d6-b773-4462-83d4-e0124c6450d6",
      "name": "付箋メモ",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -832,
        -1248
      ],
      "parameters": {
        "width": 944,
        "height": 1392,
        "content": "# 🚀 Market Research Analytics System\n\n> **Transform Google Maps data into actionable business insights with AI-powered analysis**\n\n## 📋 Overview\n\nThis n8n workflow automatically collects business data from Google Maps, analyzes customer reviews using AI, and generates comprehensive market research reports delivered straight to your inbox.\n\n---\n\n## ⚡ Quick Start Guide\n\n### Step 1: Configure Your Search Parameters\n\nNavigate to the **\"Configuration Variables\"** node and update these fields:\n\n```json\n{\n  \"search_query\": \"restaurants downtown\",        // Your target business type + location\n  \"search_location\": \"@40.7589,-73.9851,12z\",   // Coordinates (lat,lng,zoom)\n  \"language_code\": \"en\",                         // Language: en, vi, es, fr, etc.\n  \"analysis_focus\": \"restaurant\",                // Business category for analysis\n  \"city_name\": \"New York City\"                   // Target city name\n}\n```\n\n**🔍 Need coordinates?** Use [LatLong.net](https://www.latlong.net/) to find your target location coordinates.\n\n---\n\n### Step 2: Setup API Keys\n\n#### 🔑 SerpAPI (Google Maps Data)\n1. **Get API Key:** Visit [SerpAPI Google Maps Reviews](https://serpapi.com/google-maps-reviews-api)\n2. **Sign up** for free account (100 searches/month included)\n3. **Copy your API key** from the dashboard\n4. **Update workflow:** Replace `YOUR_SERPAPI_KEY_HERE` in both HTTP nodes\n\n#### 🤖 Google Gemini AI (Analysis)\n1. **Get API Key:** Visit [Google AI Studio](https://ai.google.dev/gemini-api/docs/api-key)\n2. **Create new API key** (free tier available)\n3. **Setup in n8n:** Add credentials in \"Google Gemini Chat Model\" node\n\n---\n\n### Step 3: Configure Email Delivery\n\n1. **Open** the **\"Send Email Report\"** node\n2. **Update recipient:** Change `your-email@example.com` to your email\n3. **Gmail setup:** Connect your Gmail account in n8n credentials\n\n---\n\n### Step 4: Execute & Enjoy! 🎉"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {
    "Start Workflow": [
      {}
    ]
  },
  "connections": {
    "7a561138-c149-4f92-97b7-087771017521": {
      "main": [
        [
          {
            "node": "acc1a472-587e-4dc2-b215-e986ca907163",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "68e789cd-2df5-4783-8891-5cd4d6eb0553": {
      "main": [
        [
          {
            "node": "d1084cb2-0d34-4f6a-8a51-f87b1c01c6c3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "136b7156-e38c-43a2-95d5-38a76c8e067a": {
      "main": [
        [
          {
            "node": "68e789cd-2df5-4783-8891-5cd4d6eb0553",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "31112897-af1c-4476-85b9-81a7f287a7ca",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7dd14586-d25d-4b12-baef-2be0c6987456": {
      "main": [
        [
          {
            "node": "1ecc0e85-5ec2-4f9f-a6e8-23a3f7f826a8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0cdd33f4-447d-4246-98bf-410e562686c1": {
      "main": [
        [
          {
            "node": "2ec72fea-962e-45d4-aa48-abf02c0d80de",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "31112897-af1c-4476-85b9-81a7f287a7ca": {
      "main": [
        [
          {
            "node": "c9f7f981-8476-4ba1-8448-5f29ae13d989",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "9c3ced66-5ec2-4e70-8150-e11711bc3f90": {
      "main": [
        [
          {
            "node": "0062335c-2c19-4132-938c-37a7013668ed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "c9f7f981-8476-4ba1-8448-5f29ae13d989": {
      "main": [
        [
          {
            "node": "136b7156-e38c-43a2-95d5-38a76c8e067a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0062335c-2c19-4132-938c-37a7013668ed": {
      "main": [
        [
          {
            "node": "136b7156-e38c-43a2-95d5-38a76c8e067a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "d1084cb2-0d34-4f6a-8a51-f87b1c01c6c3": {
      "main": [
        [
          {
            "node": "0cdd33f4-447d-4246-98bf-410e562686c1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2ec72fea-962e-45d4-aa48-abf02c0d80de": {
      "main": [
        [
          {
            "node": "7dd14586-d25d-4b12-baef-2be0c6987456",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "82c701cb-a88a-410c-bda4-aaaf4c5227ef": {
      "ai_languageModel": [
        [
          {
            "node": "0cdd33f4-447d-4246-98bf-410e562686c1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "acc1a472-587e-4dc2-b215-e986ca907163": {
      "main": [
        [
          {
            "node": "9c3ced66-5ec2-4e70-8150-e11711bc3f90",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
よくある質問

このワークフローの使い方は?

上記のJSON設定コードをコピーし、n8nインスタンスで新しいワークフローを作成して「JSONからインポート」を選択、設定を貼り付けて認証情報を必要に応じて変更してください。

このワークフローはどんな場面に適していますか?

中級 - 市場調査, AI要約

有料ですか?

このワークフローは完全無料です。ただし、ワークフローで使用するサードパーティサービス(OpenAI APIなど)は別途料金が発生する場合があります。

ワークフロー情報
難易度
中級
ノード数15
カテゴリー2
ノードタイプ10
難易度説明

経験者向け、6-15ノードの中程度の複雑さのワークフロー

作成者

I help Sales & Marketing teams save time with custom n8n workflows. With over 5 years of automation experience, I offer free consultations—book now!

外部リンク
n8n.ioで表示

このワークフローを共有

カテゴリー

カテゴリー: 34