├── manifest.json
├── map.png
├── popup.html
└── popup.js
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "Google Maps Easy Scrape",
4 | "version": "1.0",
5 | "description": "Google Maps Easy Scrape by Mike Powers",
6 | "permissions": ["activeTab", "scripting"],
7 | "action": {
8 | "default_popup": "popup.html"
9 | },
10 | "icons": {
11 | "16": "map.png",
12 | "48": "map.png",
13 | "128": "map.png"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/itsmikepowers/google-maps-easy-scrape/23d24d8a617f704ccab8b5456b78aed52b0bfe11/map.png
--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Google Maps Easy Scrape
5 |
6 |
72 |
73 |
74 | 🗺️ Google Maps Easy Scrape
75 |
79 | Checking URL...
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/popup.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('DOMContentLoaded', function() {
2 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
3 | var currentTab = tabs[0];
4 | var actionButton = document.getElementById('actionButton');
5 | var downloadCsvButton = document.getElementById('downloadCsvButton');
6 | var resultsTable = document.getElementById('resultsTable');
7 | var filenameInput = document.getElementById('filenameInput');
8 |
9 | if (currentTab && currentTab.url.includes("://www.google.com/maps/search")) {
10 | document.getElementById('message').textContent = "Let's scrape Google Maps!";
11 | actionButton.disabled = false;
12 | actionButton.classList.add('enabled');
13 | } else {
14 | var messageElement = document.getElementById('message');
15 | messageElement.innerHTML = '';
16 | var linkElement = document.createElement('a');
17 | linkElement.href = 'https://www.google.com/maps/search/';
18 | linkElement.textContent = "Go to Google Maps Search.";
19 | linkElement.target = '_blank';
20 | messageElement.appendChild(linkElement);
21 |
22 | actionButton.style.display = 'none';
23 | downloadCsvButton.style.display = 'none';
24 | filenameInput.style.display = 'none';
25 | }
26 |
27 | actionButton.addEventListener('click', function() {
28 | chrome.scripting.executeScript({
29 | target: {tabId: currentTab.id},
30 | function: scrapeData
31 | }, function(results) {
32 | while (resultsTable.firstChild) {
33 | resultsTable.removeChild(resultsTable.firstChild);
34 | }
35 |
36 | // Define and add headers to the table
37 | const headers = ['Title', 'Rating', 'Reviews', 'Phone', 'Industry', 'Address', 'Website', 'Google Maps Link'];
38 | const headerRow = document.createElement('tr');
39 | headers.forEach(headerText => {
40 | const header = document.createElement('th');
41 | header.textContent = headerText;
42 | headerRow.appendChild(header);
43 | });
44 | resultsTable.appendChild(headerRow);
45 |
46 | // Add new results to the table
47 | if (!results || !results[0] || !results[0].result) return;
48 | results[0].result.forEach(function(item) {
49 | var row = document.createElement('tr');
50 | ['title', 'rating', 'reviewCount', 'phone', 'industry', 'address', 'companyUrl', 'href'].forEach(function(key) {
51 | var cell = document.createElement('td');
52 |
53 | if (key === 'reviewCount' && item[key]) {
54 | item[key] = item[key].replace(/\(|\)/g, '');
55 | }
56 |
57 | cell.textContent = item[key] || '';
58 | row.appendChild(cell);
59 | });
60 | resultsTable.appendChild(row);
61 | });
62 |
63 | if (results && results[0] && results[0].result && results[0].result.length > 0) {
64 | downloadCsvButton.disabled = false;
65 | }
66 | });
67 | });
68 |
69 | downloadCsvButton.addEventListener('click', function() {
70 | var csv = tableToCsv(resultsTable);
71 | var filename = filenameInput.value.trim();
72 | if (!filename) {
73 | filename = 'google-maps-data.csv';
74 | } else {
75 | filename = filename.replace(/[^a-z0-9]/gi, '_').toLowerCase() + '.csv';
76 | }
77 | downloadCsv(csv, filename);
78 | });
79 |
80 | });
81 | });
82 |
83 |
84 | function scrapeData() {
85 | var links = Array.from(document.querySelectorAll('a[href^="https://www.google.com/maps/place"]'));
86 | return links.map(link => {
87 | var container = link.closest('[jsaction*="mouseover:pane"]');
88 | var titleText = container ? container.querySelector('.fontHeadlineSmall').textContent : '';
89 | var rating = '';
90 | var reviewCount = '';
91 | var phone = '';
92 | var industry = '';
93 | var address = '';
94 | var companyUrl = '';
95 |
96 | // Rating and Reviews
97 | if (container) {
98 | var roleImgContainer = container.querySelector('[role="img"]');
99 |
100 | if (roleImgContainer) {
101 | var ariaLabel = roleImgContainer.getAttribute('aria-label');
102 |
103 | if (ariaLabel && ariaLabel.includes("stars")) {
104 | var parts = ariaLabel.split(' ');
105 | var rating = parts[0];
106 | var reviewCount = '(' + parts[2] + ')';
107 | } else {
108 | rating = '0';
109 | reviewCount = '0';
110 | }
111 | }
112 | }
113 |
114 | // Address and Industry
115 | if (container) {
116 | var containerText = container.textContent || '';
117 | var addressRegex = /\d+ [\w\s]+(?:#\s*\d+|Suite\s*\d+|Apt\s*\d+)?/;
118 | var addressMatch = containerText.match(addressRegex);
119 |
120 | if (addressMatch) {
121 | address = addressMatch[0];
122 |
123 | // Extract industry text based on the position before the address
124 | var textBeforeAddress = containerText.substring(0, containerText.indexOf(address)).trim();
125 | var ratingIndex = textBeforeAddress.lastIndexOf(rating + reviewCount);
126 | if (ratingIndex !== -1) {
127 | // Assuming industry is the first significant text after rating and review count
128 | var rawIndustryText = textBeforeAddress.substring(ratingIndex + (rating + reviewCount).length).trim().split(/[\r\n]+/)[0];
129 | industry = rawIndustryText.replace(/[·.,#!?]/g, '').trim();
130 | }
131 | var filterRegex = /\b(Closed|Open 24 hours|24 hours)|Open\b/g;
132 | address = address.replace(filterRegex, '').trim();
133 | address = address.replace(/(\d+)(Open)/g, '$1').trim();
134 | address = address.replace(/(\w)(Open)/g, '$1').trim();
135 | address = address.replace(/(\w)(Closed)/g, '$1').trim();
136 | } else {
137 | address = '';
138 | }
139 | }
140 |
141 | // Company URL
142 | if (container) {
143 | var allLinks = Array.from(container.querySelectorAll('a[href]'));
144 | var filteredLinks = allLinks.filter(a => !a.href.startsWith("https://www.google.com/maps/place/"));
145 | if (filteredLinks.length > 0) {
146 | companyUrl = filteredLinks[0].href;
147 | }
148 | }
149 |
150 | // Phone Numbers
151 | if (container) {
152 | var containerText = container.textContent || '';
153 | var phoneRegex = /(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}/;
154 | var phoneMatch = containerText.match(phoneRegex);
155 | phone = phoneMatch ? phoneMatch[0] : '';
156 | }
157 |
158 | // Return the data as an object
159 | return {
160 | title: titleText,
161 | rating: rating,
162 | reviewCount: reviewCount,
163 | phone: phone,
164 | industry: industry,
165 | address: address,
166 | companyUrl: companyUrl,
167 | href: link.href,
168 | };
169 | });
170 | }
171 |
172 | // Convert the table to a CSV string
173 | function tableToCsv(table) {
174 | var csv = [];
175 | var rows = table.querySelectorAll('tr');
176 |
177 | for (var i = 0; i < rows.length; i++) {
178 | var row = [], cols = rows[i].querySelectorAll('td, th');
179 |
180 | for (var j = 0; j < cols.length; j++) {
181 | row.push('"' + cols[j].innerText + '"');
182 | }
183 | csv.push(row.join(','));
184 | }
185 | return csv.join('\n');
186 | }
187 |
188 | // Download the CSV file
189 | function downloadCsv(csv, filename) {
190 | var csvFile;
191 | var downloadLink;
192 |
193 | csvFile = new Blob([csv], {type: 'text/csv'});
194 | downloadLink = document.createElement('a');
195 | downloadLink.download = filename;
196 | downloadLink.href = window.URL.createObjectURL(csvFile);
197 | downloadLink.style.display = 'none';
198 | document.body.appendChild(downloadLink);
199 | downloadLink.click();
200 | }
--------------------------------------------------------------------------------