├── 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 |

76 | By Mike 77 | Buy Me A Coffee :) 78 |

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 | } --------------------------------------------------------------------------------