Skip to content

Instantly share code, notes, and snippets.

@RobinDev
Created January 31, 2025 16:12
Show Gist options
  • Save RobinDev/09368def5c9743d48c427477d2967fac to your computer and use it in GitHub Desktop.
Save RobinDev/09368def5c9743d48c427477d2967fac to your computer and use it in GitHub Desktop.
GoogleMaps Reviews Scrapper -
/**
* GoogleMaps Reviews Scrapper
* Working with gMaps in french
*
* To execute from browser console after loading a reviews page ➜
* eg : https://www.google.fr/maps/place/Tour+Eiffel/@48.8583701,2.2780018,15z/data=!4m7!3m6!1s0x47e66e2964e34e2d:0x8ddca9ee380ef7e0!8m2!3d48.8583701!4d2.2944813!15sCgt0b3VyIGVpZmZlbFoNIgt0b3VyIGVpZmZlbJIBE2hpc3RvcmljYWxfbGFuZG1hcmvgAQA!16zL20vMDJqODE?entry=tts&g_ep=EgoyMDI1MDEyOS4xIPu8ASoASAFQAw%3D%3D
*
* Note : Google will automatically prevents you to load the 2xx xxx reviews from a page.
* To extract last one, prefiltered the list.
*/
const extractShareLink = false;
const mainWrapper = document.querySelector('div[role="main"]');
const firstScrollable = findFirstScrollableElement(mainWrapper);
if (firstScrollable) await scrollToBottomUntilNoMoreScroll(firstScrollable);
console.log(await extractReviewDetails(extractShareLink));
function findFirstScrollableElement(element) {
const elements = element.querySelectorAll("*");
for (let element of elements) {
const style = window.getComputedStyle(element);
if (element.scrollHeight > element.clientHeight) {
return element;
}
}
return null; // Return null if no scrollable element is found
}
async function waitForElement(selector, timeout = 5000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const element = document.querySelector(selector);
if (element) return element;
await new Promise((resolve) => setTimeout(resolve, 100));
}
return null;
}
async function scrollToBottomUntilNoMoreScroll(element, delay = 500) {
let lastScrollHeight = element.scrollHeight;
async function scrollAndCheck() {
// Scroll to the bottom
element.scrollTop = element.scrollHeight;
await new Promise((resolve) => setTimeout(resolve, delay));
// Check if scrollHeight is the same after scrolling
if (element.scrollHeight === lastScrollHeight) {
console.log("No more scrolling possible");
return; // No more scroll
} else {
lastScrollHeight = element.scrollHeight;
await scrollAndCheck(); // Continue scrolling
}
}
await scrollAndCheck();
}
async function extractReviewDetails(extractShareLink = true) {
const reviewsData = [];
const reviews = document.querySelectorAll("div[data-review-id]");
if (!reviews.length) {
console.log("No reviews found.");
return;
}
for (const reviewContainer of reviews) {
// Extract Name: Find the first button with data-href starting with "https://www.google.com/maps/contrib/"
const nameElement = reviewContainer.querySelector(
'[data-href^="https://www.google.com/maps/contrib/"] div'
);
const name = nameElement ? nameElement.textContent : "";
console.log(!name ? "No name found !!!" : "Name:", name);
// Check for and click the "Plus" button if it exists
const expandButton = reviewContainer.querySelector(
'button[aria-expanded="false"]'
);
if (expandButton) {
expandButton.click();
await new Promise((resolve) => setTimeout(resolve, 500)); // Wait for expansion
}
// Extract Review Body: Look for the span containing the review text
const reviewBodyElement = reviewContainer.querySelector("div[id]");
const reviewBody = reviewBodyElement
? reviewBodyElement.textContent.trim()
: "";
console.log(
!reviewBodyElement ? "reviewBody not found" : "Review Body:",
reviewBody
);
// Extract Rating: Count the stars inside the rating container
const ratingElement = reviewContainer.querySelector('span[role="img"]');
const rating = ratingElement
? ratingElement.querySelectorAll(".hCCjke").length
: 0;
console.log(!ratingElement ? "Rating not found" : "Rating:", rating);
// Click on "Partager" button: Find button with aria-label containing "Partager"
let shareLink = ""; // Declare shareLink outside the if block
const shareButton = reviewContainer.querySelector(
'[aria-label*="Partager"]'
);
if (shareButton && extractShareLink) {
shareButton.click();
console.log('Clicked on "Partager"');
const inputElement = await waitForElement("input[readonly]");
shareLink = inputElement ? inputElement.value : ""; // Assign value here
console.log("Share link:", shareLink);
const closeButton = document.querySelector('button[aria-label="Fermer"]');
if (closeButton) {
closeButton.click();
console.log("Closed the popup");
} else {
console.log("Close button not found");
}
} else {
console.log("Partager button not found");
}
reviewsData.push({
name: name,
reviewBody: reviewBody,
rating: rating,
source: shareLink,
});
}
return reviewsData;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment