{
  "id": "xiabGWCXUOe469Nm",
  "name": "Google Maps Email Scraper with HTTP Requests & JavaScript",
  "tags": [
    {
      "id": "l8MPK4ZirgwFWMno",
      "name": "N8N Course",
      "createdAt": "2025-01-23T18:12:52.273Z",
      "updatedAt": "2025-01-23T18:12:52.273Z"
    }
  ],
  "nodes": [
    {
      "id": "sticky-note-1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        100,
        -280
      ],
      "parameters": {
        "width": 350,
        "height": 180,
        "content": "## \ud83d\uddfa\ufe0f STEP 1: Google Maps Data Extraction\n\nThis workflow starts by scraping Google Maps for business listings:\n\n**Process:** Uses HTTP requests to search Google Maps with queries like \"Calgary dentists\"\n**Output:** Raw HTML containing business listings and website URLs\n**Key:** No APIs required - direct HTML scraping\n\n**Note:** Replace search URL with your target location and business type"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-note-2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        300,
        -320
      ],
      "parameters": {
        "width": 380,
        "height": 200,
        "content": "## \ud83d\udd17 STEP 2: Website URL Processing\n\nExtracts and cleans business website URLs:\n\n1. **Extract URLs:** JavaScript regex finds all website URLs in Google Maps data\n2. **Filter Google URLs:** Removes irrelevant domains (google.com, gstatic, etc.)\n3. **Remove Duplicates:** Eliminates duplicate websites\n4. **Limit:** Controls batch size for testing (adjust for production)\n\n**Result:** Clean list of actual business websites ready for email extraction"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-note-3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        700,
        -320
      ],
      "parameters": {
        "width": 380,
        "height": 200,
        "content": "## \ud83d\udd04 STEP 3: Smart Website Scraping\n\nProcesses each website individually to prevent IP blocking:\n\n**Loop Over Items:** Processes websites one by one with built-in delays\n**Scrape Site:** Downloads HTML content from each business website\n**Wait Nodes:** Prevent rate limiting and IP blocking\n**Error Handling:** Continues processing even if some sites fail\n\n**Critical:** The batching and delays are essential for reliable operation at scale"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-note-4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1100,
        -340
      ],
      "parameters": {
        "width": 400,
        "height": 220,
        "content": "## \ud83d\udce7 STEP 4: Email Extraction & Export\n\nFinal processing pipeline:\n\n1. **Extract Emails:** JavaScript regex finds all email addresses in website HTML\n2. **Filter Out Empties:** Removes websites with no emails found\n3. **Split Out:** Converts email arrays into individual items\n4. **Remove Duplicates:** Final deduplication across all sources\n5. **Add to Sheet:** Exports clean email list to Google Sheets\n\n**Result:** Organized database of business emails ready for outreach"
      },
      "typeVersion": 1
    },
    {
      "id": "4a6f9faa-5b11-41fe-88c9-cc8a262a2de2",
      "name": "When clicking \u2018Test workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        140,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "47d741e7-a53f-40c7-bf7c-09a446b0d229",
      "name": "Remove Duplicates",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        940,
        0
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "6cd049c8-425e-45e0-8d45-6a4883c2f496",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        1340,
        0
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "3c3a3870-034d-4147-936a-4b0f19b7727d",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        1700,
        100
      ],
      "parameters": {
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "a377879d-517d-4c90-a3d4-a7a0c190787e",
      "name": "Limit",
      "type": "n8n-nodes-base.limit",
      "position": [
        1120,
        0
      ],
      "parameters": {
        "maxItems": 10
      },
      "typeVersion": 1
    },
    {
      "id": "5f4ae91a-29c7-4fb3-b535-34bc5194b183",
      "name": "Wait1",
      "type": "n8n-nodes-base.wait",
      "position": [
        1520,
        -60
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "645bf31c-4b3c-4f4c-b90b-610fc2d6007c",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1880,
        -60
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "emails"
      },
      "typeVersion": 1
    },
    {
      "id": "7e9c227c-2321-429a-a3f3-4c3a77bc6cf3",
      "name": "Extract Emails",
      "type": "n8n-nodes-base.code",
      "onError": "continueRegularOutput",
      "position": [
        1880,
        100
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json.data\nconst regex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.(?!jpeg|jpg|png|gif|webp|svg)[a-zA-Z]{2,}/g\nconst emails = input.match(regex)\nreturn {json: {emails:emails}}"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "fb76c70c-385d-4e7f-93cd-43023690076a",
      "name": "Extract URLs",
      "type": "n8n-nodes-base.code",
      "position": [
        540,
        0
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json.data\nconst regex = /https?:\\/\\/[^\\/\\s\"'>]+/g\nconst websites = input.match(regex)\nreturn websites.map(website => ({json:{website}}))"
      },
      "typeVersion": 2
    },
    {
      "id": "b10c0ed6-5a94-4272-b65c-bd19bd674fa0",
      "name": "Scrape Google Maps",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        340,
        0
      ],
      "parameters": {
        "url": "https://www.google.com/maps/search/calgary+dentists",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          },
          "allowUnauthorizedCerts": true
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "58a0d85d-f6eb-47d1-ba9c-3af69ddd30fb",
      "name": "Scrape Site",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        1520,
        100
      ],
      "parameters": {
        "url": "={{ $json.website }}",
        "options": {
          "redirect": {
            "redirect": {
              "followRedirects": false
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4618e775-d14d-4355-b3a2-cd6a6bbc70e8",
      "name": "Filter Out Empties",
      "type": "n8n-nodes-base.filter",
      "position": [
        1700,
        -60
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a6786c58-424a-409a-b87f-8a7592cb7944",
              "operator": {
                "type": "array",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.emails }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "74513e7e-a7dc-4b19-b23e-01efd82c9d6d",
      "name": "Filter Google URLs",
      "type": "n8n-nodes-base.filter",
      "position": [
        740,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "bf0a5053-9660-457c-9581-964793bb6d7d",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "schema"
            },
            {
              "id": "9110b9e0-12aa-45cc-bde0-9eda8c10970e",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "google"
            },
            {
              "id": "fb9b6ed6-96a5-4560-ab10-b8a4b9a61a2b",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "gg"
            },
            {
              "id": "10500c0b-cdbd-4816-aba3-df60d69845dc",
              "operator": {
                "type": "string",
                "operation": "notContains"
              },
              "leftValue": "={{ $json.website }}",
              "rightValue": "gstatic"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "035fbbe8-7758-4c7b-be8e-c1cba8fc48bc",
      "name": "Remove Duplicates (2)",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        2080,
        -60
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "aa3e9285-235d-4fda-ab0e-45134cf825dd",
      "name": "Add to Sheet (or whatever you want!)",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2280,
        -60
      ],
      "parameters": {
        "columns": {
          "value": {
            "emails": "={{ $json.emails }}"
          },
          "schema": [
            {
              "id": "emails",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "emails",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "emails"
          ]
        },
        "options": {
          "useAppend": true
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fcijyZM1oU73i2xUbXYJ4j6RshmVEduOkCJji2SJP68/edit#gid=0",
          "cachedResultName": "emails"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1fcijyZM1oU73i2xUbXYJ4j6RshmVEduOkCJji2SJP68",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1fcijyZM1oU73i2xUbXYJ4j6RshmVEduOkCJji2SJP68/edit?usp=drivesdk",
          "cachedResultName": "Scrape WITHOUT Paying for APIs"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "9e487a0d-a606-4859-a6e2-b70372b61388",
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Extract Emails",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait1": {
      "main": [
        [
          {
            "node": "Filter Out Empties",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Remove Duplicates (2)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Site": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract URLs": {
      "main": [
        [
          {
            "node": "Filter Google URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Emails": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Wait1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Scrape Site",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicates": {
      "main": [
        [
          {
            "node": "Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Google URLs": {
      "main": [
        [
          {
            "node": "Remove Duplicates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Out Empties": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Google Maps": {
      "main": [
        [
          {
            "node": "Extract URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicates (2)": {
      "main": [
        [
          {
            "node": "Add to Sheet (or whatever you want!)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Test workflow\u2019": {
      "main": [
        [
          {
            "node": "Scrape Google Maps",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}