n8n workflow: Google Maps Email Scraper Template

Nodes:
name: Remove Duplicate URLs
type: n8n-nodes-base.removeDuplicates
name: Loop over queries
type: n8n-nodes-base.splitInBatches
name: Search Google Maps with query
type: n8n-nodes-base.httpRequest
parameters: url: =https://www.google.com/maps/search/{{ $json.query }}
options: allowUnauthorizedCerts: no
executeOnce: no
alwaysOutputData: no
name: Scrape URLs from results
type: n8n-nodes-base.code
parameters: jsCode: const data = $input.first().json.data

const regex = /https?:\/\/[^\/]+/g

const urls = data.match(regex)

return urls.map(url => ({json: {url: url}}))
name: Filter irrelevant URLs
type: n8n-nodes-base.filter
parameters: conditions: options: version: 2
caseSensitive: yes
typeValidation: strict
combinator: and
conditions: operator: type: string
operation: notRegex
leftValue: ={{ $json.url }}
rightValue: =(google|gstatic|ggpht|schema\.org|example\.com|sentry-next\.wixpress\.com|imli\.com|sentry\.wixpress\.com|ingest\.sentry\.io)
operator: name: filter.operator.equals
type: string
operation: equals
name: Request web page for URL
type: n8n-nodes-base.httpRequest
onError: continueRegularOutput
parameters: url: ={{ $json.url }}
alwaysOutputData: no
name: Loop over URLs
type: n8n-nodes-base.splitInBatches
onError: continueErrorOutput
parameters: options: reset: no
name: Loop over pages
type: n8n-nodes-base.splitInBatches
onError: continueErrorOutput
alwaysOutputData: no
name: Scrape emails from page
type: n8n-nodes-base.code
onError: continueRegularOutput
parameters: mode: runOnceForEachItem
jsCode: const data = $json.data

const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.(?!png|jpg|gif|jpeg)[a-zA-Z]{2,}/g

const emails = data.match(emailRegex)

return {json: {emails: emails}}
name: Aggregate arrays of emails
type: n8n-nodes-base.aggregate
parameters: options: mergeLists: yes
fieldsToAggregate: fieldToAggregate: emails
name: Split out into default data structure
type: n8n-nodes-base.splitOut
parameters: fieldToSplitOut: emails
name: Remove duplicate emails
type: n8n-nodes-base.removeDuplicates
parameters: compare: selectedFields
fieldsToCompare: emails
name: Filter irrelevant emails
type: n8n-nodes-base.filter
parameters: conditions: options: version: 2
caseSensitive: yes
typeValidation: strict
combinator: and
conditions: operator: type: string
operation: notRegex
leftValue: ={{ $json.emails }}
rightValue: =(google|gstatic|ggpht|schema\.org|example\.com|sentry\.wixpress\.com|sentry-next\.wixpress\.com|ingest\.sentry\.io|sentry\.io|imli\.com)
name: Save emails to Google Sheet
type: n8n-nodes-base.googleSheets
parameters: columns: value: Emails: ={{ $json.emails }}
schema: type: string
display: yes
removed: no
required: no
displayName: Emails
defaultMatch: no
canBeUsedToMatch: yes
mappingMode: defineBelow
matchingColumns: Emails
operation: append
name: Starts scraper workflow
type: n8n-nodes-base.executeWorkflowTrigger
name: Run workflow
type: n8n-nodes-base.manualTrigger
name: Wait between executions
type: n8n-nodes-base.wait
parameters: amount: 2
name: Execute scraper for query
type: n8n-nodes-base.executeWorkflow
parameters: mode: each
options: waitForSubWorkflow: no
workflowId: __rl: yes
mode: id
value: ={{ $workflow.id }}
name: Sticky Note
type: n8n-nodes-base.stickyNote
parameters: color: 5
width: 740
height: 180
content: ## 🛠 Setup
1. Setup your list of queries in the "Run workflow" manual trigger node. Watch  this [video](https://youtu.be/HaiO-UeiKBA) on how to generate the queries with ChatGPT.
3. Choose a sheet to populate with data in the **Google Sheets node**
4. Run the workflow and start getting leads into your Google Sheets document
name: Sticky Note2
type: n8n-nodes-base.stickyNote
parameters: color: 6
height: 100
content: **Optional** 👇
Set wait time between each query workflow execution. Default is 2 seconds.
name: Sticky Note3
type: n8n-nodes-base.stickyNote
parameters: width: 480
height: 100
content: ### Scraper 👇
This workflow will be executed in the background for each query. Click the **All executions** tab in the left sidebar to see the executions running.
name: Sticky Note4
type: n8n-nodes-base.stickyNote
parameters: color: 4
height: 180
content: 👆 
1. Setup your **credentials**. Here's a [video tutorial](https://youtu.be/O5RnWDM27M8) on how to do that.

2. Choose which document and sheet to save the scraped emails to. 
name: Sticky Note5
type: n8n-nodes-base.stickyNote
parameters: color: 3
content:  ## ⚠️ Note

A [video tutorial](https://youtu.be/HaiO-UeiKBA) for this workflow guide is available on my [Youtube channel](https://www.youtube.com/channel/UCn8xmUBunez1SsDVRfZDUGA)
name: Sticky Note6
type: n8n-nodes-base.stickyNote
parameters: color: 7
width: 480
height: 140
content: ## Google Maps Automatic Email Scraper

This workflow automatically scrapes emails from businesses on Google Maps based on a list of queries that you provide.
name: Sticky Note7
type: n8n-nodes-base.stickyNote
parameters: color: 6
width: 160
height: 100
content: **Optional** 👆
Add or change the regex for filtering irrelevant URLs.
name: Sticky Note8
type: n8n-nodes-base.stickyNote
parameters: color: 6
width: 200
height: 100
content: **Optional** 👆
Add or change the regex for filtering irrelevant/incorrect email addresses.

Connections:
Run workflow: main: node: Loop over queries
type: main
Loop over URLs: main: node: Loop over pages
type: main
node: Request web page for URL
type: main
Loop over pages: main: node: Aggregate arrays of emails
type: main
node: Scrape emails from page
type: main
Loop over queries: main: node: Execute scraper for query
type: main
Remove Duplicate URLs: main: node: Loop over URLs
type: main
Filter irrelevant URLs: main: node: Remove Duplicate URLs
type: main
Remove duplicate emails: main: node: Filter irrelevant emails
type: main
Scrape emails from page: main: node: Loop over pages
type: main
Starts scraper workflow: main: node: Search Google Maps with query
type: main
Wait between executions: main: node: Loop over queries
type: main
Filter irrelevant emails: main: node: Save emails to Google Sheet
type: main
Request web page for URL: main: node: Loop over URLs
type: main
Scrape URLs from results: main: node: Filter irrelevant URLs
type: main
Execute scraper for query: main: node: Wait between executions
type: main
Aggregate arrays of emails: main: node: Split out into default data structure
type: main
Search Google Maps with query: main: node: Scrape URLs from results
type: main
Split out into default data structure: main: node: Remove duplicate emails
type: main