Greasy Fork is available in English.
Scrapes reviews from Google Maps, expands truncated content, and exports to clipboard
// ==UserScript== // @name Google Reviews Batch to JSON // @namespace http://tampermonkey.net/ // @version 0.9 // @description Scrapes reviews from Google Maps, expands truncated content, and exports to clipboard // @author sharmanhall // @match https://www.google.com/maps/place/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; const scrapeReviews = async () => { const reviewDivs = document.querySelectorAll("div[data-review-id]"); const reviews = []; for (const reviewDiv of reviewDivs) { const review = {}; const reviewerName = reviewDiv.querySelector("div.d4r55"); if (reviewerName) review.reviewer_name = reviewerName.textContent.trim(); const img = reviewDiv.querySelector("img.NBa7we"); if (img) review.img_url = img.src; const dateSpan = reviewDiv.querySelector("span.rsqaWe"); if (dateSpan) review.review_date = dateSpan.textContent.trim(); const stars = reviewDiv.querySelectorAll("img.hCCjke.vzX5Ic"); if (stars) review.star_rating = stars.length; const reviewButton = reviewDiv.querySelector("button[data-href]"); if (reviewButton) review.review_url = reviewButton.getAttribute("data-href"); let reviewContentSpan = reviewDiv.querySelector("span.wiI7pd"); if (reviewContentSpan) { const moreButton = reviewDiv.querySelector("button.w8nwRe.kyuRq"); if (moreButton) { moreButton.click(); await new Promise(r => setTimeout(r, 500)); } reviewContentSpan = reviewDiv.querySelector("span.wiI7pd"); review.review_content = reviewContentSpan.textContent.trim(); } reviews.push(review); } console.log("%c Scrape R###lts:", "font-size: 24px; font-weight: bold;"); reviews.forEach((review, idx) => { console.log(`%c Review #${idx+1}`, "font-size: 20px; font-weight: bold;"); console.log(`%cReviewer Name: ${review.reviewer_name}`, "font-size: 18px;"); console.log(`%cImage URL: ${review.img_url}`, "font-size: 18px;"); console.log(`%cReview Date: ${review.review_date}`, "font-size: 18px;"); console.log(`%cStar Rating: ${review.star_rating}`, "font-size: 18px;"); console.log(`%cReview URL: ${review.review_url}`, "font-size: 18px; color: blue;"); console.log(`%cReview Content: ${review.review_content}`, "font-size: 18px;"); console.log(" "); }); return reviews; }; const copyToClipboard = async () => { const reviews = await scrapeReviews(); const contentToCopy = JSON.stringify(reviews, null, 2); navigator.clipboard.writeText(contentToCopy).then(() => { console.log("%c Content copied to clipboard!", "font-size: 24px; font-weight: bold; color: green;"); }).catch(err => { console.error("Could not copy content to clipboard: ", err); }); }; const createButton = (label, actionFunc) => { const button = document.createElement("button"); button.className = "g88MCb S9kvJb"; button.setAttribute("aria-label", label); button.innerHTML = `<span class="DVeyrd "><div class="OyjIsf zemfqc"></div><span class="Cw1rxd google-symbols"></span><span class="GMtm7c fontTitleSmall">${label}</span></span>`; button.onclick = actionFunc; return button; } const scrapeButton = createButton("Scrape Reviews", scrapeReviews); const copyButton = createButton("Copy to Clipboard", copyToClipboard); const writeReviewDiv = document.querySelector("div.m6QErb.Hk4XGb.QoaCgb.KoSBEe.tLjsW"); if (writeReviewDiv) { [scrapeButton, copyButton].forEach(btn => { const buttonWrapper = document.createElement("div"); buttonWrapper.className = "TrU0dc kdfrQc"; buttonWrapper.appendChild(btn); writeReviewDiv.appendChild(buttonWrapper); }); } })();