diff --git a/README.md b/README.md
index 05e1e86e..5732652f 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,5 @@
# Google Maps Scraper
-
-
-
-
-
-
[](https://github.com/gosom/google-maps-scraper/actions/workflows/build.yml)
[](https://goreportcard.com/report/github.com/gosom/google-maps-scraper)
[](https://godoc.org/github.com/gosom/google-maps-scraper)
@@ -27,100 +21,6 @@ Use it for lead generation, local business research, sales prospecting, data enr

-If this project is useful to you, a GitHub star helps others discover it. Sponsorships help fund maintenance and new work.
-
----
-
-## Sponsored By
-
-This project is made possible by our amazing sponsors
-
-### [Scrap.io](https://scrap.io?utm_medium=ads&utm_source=github_gosom_gmap_scraper) - Extract ALL Google Maps listings at country-scale
-
-[](https://scrap.io?utm_medium=ads&utm_source=github_gosom_gmap_scraper)
-
-No keywords needed. No limits. Export millions of businesses in 2 clicks. [**Try it free →**](https://scrap.io?utm_medium=ads&utm_source=github_gosom_gmap_scraper)
-
----
-
-### [G Maps Extractor](https://gmapsextractor.com?utm_source=github&utm_medium=banner&utm_campaign=gosom) - No-code Google Maps scraper
-
-[](https://gmapsextractor.com?utm_source=github&utm_medium=banner&utm_campaign=gosom)
-
-Chrome extension that extracts emails, social profiles, phone numbers, reviews & more. [**Get 1,000 free leads →**](https://gmapsextractor.com?utm_source=github&utm_medium=banner&utm_campaign=gosom)
-
----
-
-### [SerpApi](https://serpapi.com/?utm_source=google-maps-scraper) - Google Maps API and 30+ search engine APIs
-
-[](https://serpapi.com/?utm_source=google-maps-scraper)
-
-Fast, reliable, and scalable. Used by Fortune 500 companies. [**View all APIs →**](https://serpapi.com/search-api)
-
----
-
-### [SearchApi](https://www.searchapi.io/google-maps?via=gosom) - Google Maps API for SERP scraping
-
-[](https://www.searchapi.io/google-maps?via=gosom)
-
-Real-time Google Maps data with a simple integration. [**Explore the API →**](https://www.searchapi.io/google-maps?via=gosom)
-
----
-
-### [Evomi](https://evomi.com?utm_source=github&utm_medium=banner&utm_campaign=gosom-maps) - Swiss quality proxies for scraping
-
-[](https://evomi.com?utm_source=github&utm_medium=banner&utm_campaign=gosom-maps)
-
-Swiss quality proxies from $0.49/GB across 150+ countries, with 24/7 support and 99.9% uptime. [**Visit Evomi →**](https://evomi.com?utm_source=github&utm_medium=banner&utm_campaign=gosom-maps)
-
----
-
-### [HasData](https://hasdata.com/scrapers/google-maps?utm_source=github&utm_medium=sponsorship&utm_campaign=gosom) - No-code Google Maps Scraper & Email Extraction
-
-[](https://hasdata.com/scrapers/google-maps?utm_source=github&utm_medium=sponsorship&utm_campaign=gosom)
-
-Extract business leads, emails, addresses, phones, reviews and more. [**Get 1,000 free credits →**](https://hasdata.com/scrapers/google-maps?utm_source=github&utm_medium=sponsorship&utm_campaign=gosom)
-
----
-
-### [RapidProxy](https://www.rapidproxy.io/?ref=gosom) - High-Performance Proxy Solution
-
-[](https://www.rapidproxy.io/?ref=gosom)
-
-Unlock global access with consistent, high-speed connections from $0.65/GB, 90M+ real residential IPs worldwide, and traffic that never expires. [**Try it free →**](https://www.rapidproxy.io/?ref=gosom)
-
----
-
-### [LeadsDB](https://getleadsdb.com/) - Your Central Database for Business Leads
-
-[](https://getleadsdb.com/)
-
-Push leads via API or AI agent, remove duplicates automatically, and export when ready. [**Start free →**](https://getleadsdb.com/)
-
----
-
-### [Webshare](https://www.webshare.io/?referral_code=0q3l81eet8mp) - Premium proxies for scraping at scale
-
-[](https://www.webshare.io/?referral_code=0q3l81eet8mp)
-
-The most affordable premium proxies across 195 countries & 80+ million IPs, plus a FREE plan for new users. [Learn more](webshare.md)
-
----
-
-### [Legion Proxy](https://legionproxy.io/?utm_source=github&utm_campaign=gmaps) - Residential proxies for Google Maps Scraper
-
-74M+ real residential IPs, HTTP/3 & UDP support, SOCKS5-ready, 195+ countries. Works out of the box with Google Maps Scraper. [**Get 1GB free →**](https://legionproxy.io/?utm_source=github&utm_campaign=gmaps)
-
-[](https://legionproxy.io/?utm_source=github&utm_campaign=gmaps)
-
----
-
-
- View all sponsors | Become a sponsor
-
-
----
-
## Why Use This Scraper?
| | |
@@ -128,28 +28,13 @@ The most affordable premium proxies across 195 countries & 80+ million IPs, plus
| **Completely Free & Open Source** | MIT licensed, no hidden costs or usage limits |
| **Multiple Interfaces** | CLI, Web UI, REST API - use what fits your workflow |
| **High Performance** | ~120 places/minute with optimized concurrency |
-| **33+ Data Points** | Business details, reviews, emails, coordinates, and more |
+| **40+ Output Fields** | Business details, reviews, review-removal notices, emails, coordinates, and more |
| **Production Ready** | Scale from a single machine to Kubernetes clusters |
-| **Flexible Output** | CSV, JSON, PostgreSQL, S3, LeadsDB, or custom plugins |
+| **Flexible Output** | CSV, JSON, PostgreSQL, S3, or custom plugins |
| **Proxy Support** | Built-in SOCKS5/HTTP/HTTPS proxy rotation |
---
-## What's Next After Scraping?
-
-Once you've collected your data, you'll need to manage, deduplicate, and work with your leads. **[LeadsDB](https://getleadsdb.com/)** is a companion tool designed exactly for this:
-
-- **Automatic Deduplication** - Import from multiple scrapes without worrying about duplicates
-- **AI Agent Ready** - Query and manage leads with natural language via MCP
-- **Advanced Filtering** - Combine filters with AND/OR logic on any field
-- **Export Anywhere** - CSV, JSON, or use the REST API
-
-The scraper has [built-in LeadsDB integration](#export-to-leadsdb) - just add your API key and leads flow directly into your database.
-
-**[Start free with 500 leads](https://getleadsdb.com/)**
-
----
-
## Table of Contents
- [Quick Start](#quick-start)
@@ -159,7 +44,6 @@ The scraper has [built-in LeadsDB integration](#export-to-leadsdb) - just add yo
- [SaaS Edition](#saas-edition)
- [AI Agent Skill](#ai-agent-skill)
- [Recipes](docs/recipes.md)
-- [Proxy Sponsors](docs/proxies.md)
- [Installation](#installation)
- [Features](#features)
- [Extracted Data Points](#extracted-data-points)
@@ -168,13 +52,11 @@ The scraper has [built-in LeadsDB integration](#export-to-leadsdb) - just add yo
- [Using Proxies](#using-proxies)
- [Email Extraction](#email-extraction)
- [Fast Mode](#fast-mode)
-- [Export to LeadsDB](#export-to-leadsdb)
- [Advanced Usage](#advanced-usage)
- [PostgreSQL Database Provider](#postgresql-database-provider)
- [Kubernetes Deployment](#kubernetes-deployment)
- [Custom Writer Plugins](#custom-writer-plugins)
- [Performance](#performance)
-- [Support the Project](#support-the-project)
- [Community](#community)
- [Contributing](#contributing)
- [License](#license)
@@ -211,19 +93,6 @@ Useful options:
`-c` controls how many scrape jobs run in parallel. Higher concurrency can finish large input files faster, but it also uses more CPU/RAM and can increase blocking or failures, especially without proxies. Start with the default for a first run. For larger jobs on a capable machine, try `-c 4`, `-c 8`, or `-c 16` and measure the result.
-**Want to skip CSV files?** Send leads directly to [LeadsDB](https://getleadsdb.com/):
-
-```bash
-docker run \
- -v gmaps-playwright-cache:/opt \
- -v "$PWD/example-queries.txt:/queries.txt:ro" \
- gosom/google-maps-scraper \
- -input /queries.txt \
- -depth 1 \
- -leadsdb-api-key "your-api-key" \
- -exit-on-inactivity 3m
-```
-
### Web UI
Start the web interface with a single command:
@@ -271,7 +140,7 @@ curl -fsSL https://raw.githubusercontent.com/gosom/google-maps-scraper/main/PROV
See [SaaS documentation](docs/saas.md) for deployment and operations details.
There is also a [5-minute deployment walkthrough](https://gosom.dev/deploy-your-own-maps-scraping-api-in-5-minutes/) and a [YouTube video walkthrough](https://www.youtube.com/watch?v=STG9mZw_nac).
-More examples are available in [Recipes](docs/recipes.md). If you need proxies for larger jobs, see [Proxy Sponsors](docs/proxies.md).
+More examples are available in [Recipes](docs/recipes.md).
---
@@ -326,9 +195,9 @@ go build
| Feature | Description |
|---------|-------------|
-| **33+ Data Points** | Business name, address, phone, website, reviews, coordinates, and more |
+| **40+ Output Fields** | Business name, address, phone, website, reviews, review-removal notices, coordinates, and more |
| **Email Extraction** | Optional crawling of business websites for email addresses |
-| **Multiple Output Formats** | CSV, JSON, PostgreSQL, S3, LeadsDB, or custom plugins |
+| **Multiple Output Formats** | CSV, JSON, PostgreSQL, S3, or custom plugins |
| **Proxy Support** | SOCKS5, HTTP, HTTPS with authentication |
| **Scalable Architecture** | Single machine to Kubernetes cluster |
| **REST API** | Programmatic control for automation |
@@ -341,7 +210,7 @@ go build
## Extracted Data Points
-Click to expand all 33 data points
+Click to expand available output fields
| # | Field | Description |
|---|-------|-------------|
@@ -358,30 +227,41 @@ go build
| 11 | `review_count` | Total number of reviews |
| 12 | `review_rating` | Average star rating |
| 13 | `reviews_per_rating` | Breakdown by star rating |
-| 14 | `latitude` | GPS latitude |
-| 15 | `longitude` | GPS longitude |
-| 16 | `cid` | Google's unique Customer ID |
-| 17 | `status` | Business status (open/closed/temporary) |
-| 18 | `descriptions` | Business description |
-| 19 | `reviews_link` | Direct link to reviews |
-| 20 | `thumbnail` | Thumbnail image URL |
-| 21 | `timezone` | Business timezone |
-| 22 | `price_range` | Price level ($, $$, $$$) |
-| 23 | `data_id` | Internal Google Maps identifier |
-| 24 | `images` | Associated image URLs |
-| 25 | `reservations` | Reservation booking link |
-| 26 | `order_online` | Online ordering link |
-| 27 | `menu` | Menu link |
-| 28 | `owner` | Owner-claimed status |
-| 29 | `complete_address` | Full formatted address |
-| 30 | `about` | Additional business info |
-| 31 | `user_reviews` | Customer reviews (text, rating, timestamp) |
-| 32 | `emails` | Extracted email addresses (requires `-email` flag) |
-| 33 | `user_reviews_extended` | Extended reviews up to ~300 (requires `-extra-reviews`) |
-| 34 | `place_id` | Google's unique place id |
+| 14 | `reviews_1_star` | One-star review count, flattened for CSV output |
+| 15 | `reviews_2_star` | Two-star review count, flattened for CSV output |
+| 16 | `reviews_3_star` | Three-star review count, flattened for CSV output |
+| 17 | `reviews_4_star` | Four-star review count, flattened for CSV output |
+| 18 | `reviews_5_star` | Five-star review count, flattened for CSV output |
+| 19 | `removed_reviews_min` | Lower bound of reviews Google reports as removed under a review-removal notice |
+| 20 | `removed_reviews_max` | Upper bound of reviews Google reports as removed; `0` means no upper bound was present |
+| 21 | `latitude` | GPS latitude |
+| 22 | `longitude` | GPS longitude |
+| 23 | `cid` | Google's unique Customer ID |
+| 24 | `status` | Business status (open/closed/temporary) |
+| 25 | `descriptions` | Business description |
+| 26 | `reviews_link` | Direct link to reviews |
+| 27 | `thumbnail` | Thumbnail image URL |
+| 28 | `timezone` | Business timezone |
+| 29 | `price_range` | Price level ($, $$, $$$) |
+| 30 | `data_id` | Internal Google Maps identifier |
+| 31 | `images` | Associated image URLs |
+| 32 | `reservations` | Reservation booking link |
+| 33 | `order_online` | Online ordering link |
+| 34 | `menu` | Menu link |
+| 35 | `owner` | Owner-claimed status |
+| 36 | `complete_address` | Full formatted address |
+| 37 | `about` | Additional business info |
+| 38 | `user_reviews` | Customer reviews (text, rating, timestamp) |
+| 39 | `emails` | Extracted email addresses (requires `-email` flag) |
+| 40 | `user_reviews_extended` | Extended reviews up to ~300 (requires `-extra-reviews`) |
+| 41 | `place_id` | Google's unique place id |
+The `removed_reviews_min` and `removed_reviews_max` fields are parsed from Google Maps review-removal notices when Google includes that notice in the Maps payload. For example, a notice such as `201 to 250 reviews were removed` is exported as `removed_reviews_min=201` and `removed_reviews_max=250`; an open-ended notice such as `over 250` is exported as `removed_reviews_min=251` and `removed_reviews_max=0`. When no notice is present, both fields remain `0`.
+
+JSON output includes the full `reviews_per_rating` map plus `removed_reviews_min` and `removed_reviews_max`. CSV output also includes the flattened `reviews_1_star` through `reviews_5_star` columns for easier spreadsheet analysis.
+
**Custom Input IDs:** Define your own IDs in the input file:
```
Matsuhisa Athens #!#MyCustomID
@@ -428,9 +308,6 @@ Proxy:
-proxies string Comma-separated proxy list
Format: protocol://user:pass@host:port
-Export:
- -leadsdb-api-key Export directly to LeadsDB (get key at getleadsdb.com)
-
Advanced:
-exit-on-inactivity duration Exit after inactivity (e.g., '5m')
-fast-mode Quick mode with reduced data
@@ -458,8 +335,6 @@ For larger scraping jobs, proxies help avoid rate limiting. Here's how to config
**Supported protocols:** `socks5`, `socks5h`, `http`, `https`
-Current proxy sponsors are listed in [Proxy Sponsors](docs/proxies.md). Using those links helps fund project maintenance.
-
### Email Extraction
Email extraction is **disabled by default**. When enabled, the scraper visits each business website to find email addresses.
@@ -515,62 +390,6 @@ Notes:
---
-## Export to LeadsDB
-
-Skip the CSV files and send leads directly to a managed database. [LeadsDB](https://getleadsdb.com/) handles deduplication, filtering, and provides an API for your applications.
-
-**Using Docker:**
-```bash
-docker run \
- -v gmaps-playwright-cache:/opt \
- -v "$PWD/example-queries.txt:/queries.txt:ro" \
- gosom/google-maps-scraper \
- -input /queries.txt \
- -depth 1 \
- -leadsdb-api-key "your-api-key" \
- -exit-on-inactivity 3m
-```
-
-**Using binary:**
-```bash
-./google-maps-scraper \
- -input queries.txt \
- -leadsdb-api-key "your-api-key" \
- -exit-on-inactivity 3m
-```
-
-Or via environment variable:
-```bash
-export LEADSDB_API_KEY="your-api-key"
-./google-maps-scraper -input queries.txt -exit-on-inactivity 3m
-```
-
-
-Field Mapping
-
-| Google Maps | LeadsDB |
-|-------------|---------|
-| Title | Name |
-| Category | Category |
-| Categories | Tags |
-| Phone | Phone |
-| Website | Website |
-| Address | Address, City, State, Country, PostalCode |
-| Latitude/Longitude | Coordinates |
-| Review Rating | Rating |
-| Review Count | ReviewCount |
-| Emails | Email |
-| Thumbnail | LogoURL |
-| CID | SourceID |
-
-Additional fields (Google Maps link, plus code, price range, etc.) are stored as custom attributes.
-
-
-
-Get your API key at [getleadsdb.com/settings](https://getleadsdb.com/settings) after signing up.
-
----
-
## Advanced Usage
### PostgreSQL Database Provider
@@ -665,20 +484,6 @@ Anonymous usage statistics are collected for improvement purposes. Opt out:
export DISABLE_TELEMETRY=1
```
----
-
-## Support the Project
-
-This project is free and open source. Stars, sponsorships, and sponsor referrals help fund maintenance.
-
-- Star the repository: [github.com/gosom/google-maps-scraper](https://github.com/gosom/google-maps-scraper)
-- Sponsor development: [GitHub Sponsors](https://github.com/sponsors/gosom)
-- Need proxies? See [Proxy Sponsors](docs/proxies.md)
-- Deploying the self-hosted platform? See [SaaS deployment options](docs/saas.md)
-- Managing scraped leads? See [LeadsDB](https://getleadsdb.com/)
-
----
-
## Community
[](https://discord.gg/fpaAVhNCCu)
@@ -718,20 +523,6 @@ See [AGENTS.md](AGENTS.md) for development guidelines.
This project is licensed under the [MIT License](LICENSE).
----
-
-## Star History
-
-
-
-
-
-
-
-
-
----
-
## Legal Notice
Please use this scraper responsibly and in accordance with applicable laws and regulations. Unauthorized scraping may violate terms of service.
diff --git a/gmaps/entry.go b/gmaps/entry.go
index 007f2270..cc8ba814 100644
--- a/gmaps/entry.go
+++ b/gmaps/entry.go
@@ -6,12 +6,19 @@ import (
"iter"
"math"
"net/url"
+ "regexp"
"runtime/debug"
"slices"
"strconv"
"strings"
)
+var (
+ removedReviewNumericRangeRe = regexp.MustCompile(`([0-9][0-9,.]*)[[:space:]]*(to|-|bis)[[:space:]]*([0-9][0-9,.]*)`)
+ removedReviewOpenEndedRe = regexp.MustCompile(`(über|ueber|mehr als|over|more than|above)[[:space:]]+([0-9][0-9,.]*)`)
+ removedReviewSingleRe = regexp.MustCompile(`(^|\b)(one|1|eine)[[:space:]]+(review|bewertung|rezension)`)
+)
+
type Image struct {
Title string `json:"title"`
Image string `json:"image"`
@@ -75,6 +82,8 @@ type Entry struct {
ReviewCount int `json:"review_count"`
ReviewRating float64 `json:"review_rating"`
ReviewsPerRating map[int]int `json:"reviews_per_rating"`
+ RemovedReviewsMin int `json:"removed_reviews_min"`
+ RemovedReviewsMax int `json:"removed_reviews_max"`
Latitude float64 `json:"latitude"`
Longtitude float64 `json:"longtitude"`
Status string `json:"status"`
@@ -171,6 +180,13 @@ func (e *Entry) CsvHeaders() []string {
"review_count",
"review_rating",
"reviews_per_rating",
+ "reviews_1_star",
+ "reviews_2_star",
+ "reviews_3_star",
+ "reviews_4_star",
+ "reviews_5_star",
+ "removed_reviews_min",
+ "removed_reviews_max",
"latitude",
"longitude",
"cid",
@@ -210,6 +226,13 @@ func (e *Entry) CsvRow() []string {
stringify(e.ReviewCount),
stringify(e.ReviewRating),
stringify(e.ReviewsPerRating),
+ stringify(e.ReviewsPerRating[1]),
+ stringify(e.ReviewsPerRating[2]),
+ stringify(e.ReviewsPerRating[3]),
+ stringify(e.ReviewsPerRating[4]),
+ stringify(e.ReviewsPerRating[5]),
+ stringify(e.RemovedReviewsMin),
+ stringify(e.RemovedReviewsMax),
stringify(e.Latitude),
stringify(e.Longtitude),
e.Cid,
@@ -434,6 +457,7 @@ func EntryFromJSON(raw []byte, reviewCountOnly ...bool) (entry Entry, err error)
4: int(getNthElementAndCast[float64](darray, 175, 3, 3)),
5: int(getNthElementAndCast[float64](darray, 175, 3, 4)),
}
+ entry.RemovedReviewsMin, entry.RemovedReviewsMax = getRemovedReviewsRange(darray)
// Parse inline reviews from the page data
reviewsI := getNthElementAndCast[[]any](darray, 175, 9, 0, 0)
@@ -452,6 +476,92 @@ func EntryFromJSON(raw []byte, reviewCountOnly ...bool) (entry Entry, err error)
return entry, nil
}
+func getRemovedReviewsRange(darray []any) (int, int) {
+ noticesI := getNthElementAndCast[[]any](darray, 241, 0)
+ if len(noticesI) == 0 {
+ return 0, 0
+ }
+
+ for i := range noticesI {
+ noticeI := getNthElementAndCast[[]any](noticesI, i)
+ if int(getNthElementAndCast[float64](noticeI, 0)) != 3 {
+ continue
+ }
+
+ noticeBody := getNthElementAndCast[[]any](noticeI, 1)
+ for _, textIndex := range []int{0, 3} {
+ minReviews, maxReviews := parseRemovedReviewsRange(getNthElementAndCast[string](noticeBody, textIndex))
+ if minReviews != 0 || maxReviews != 0 {
+ return minReviews, maxReviews
+ }
+ }
+ }
+
+ return 0, 0
+}
+
+func parseRemovedReviewsRange(text string) (int, int) {
+ lower := strings.ToLower(text)
+ if matches := removedReviewNumericRangeRe.FindStringSubmatch(lower); len(matches) == 4 {
+ minReviews, minOK := parseReviewRangeNumber(matches[1])
+ maxReviews, maxOK := parseReviewRangeNumber(matches[3])
+ if minOK && maxOK {
+ return minReviews, maxReviews
+ }
+ }
+
+ if matches := removedReviewOpenEndedRe.FindStringSubmatch(lower); len(matches) == 3 {
+ minReviews, ok := parseReviewRangeNumber(matches[2])
+ if ok {
+ return minReviews + 1, 0
+ }
+ }
+
+ if removedReviewSingleRe.MatchString(lower) {
+ return 1, 1
+ }
+
+ fields := strings.Fields(lower)
+ for i := 0; i+2 < len(fields); i++ {
+ if fields[i+1] != "to" {
+ continue
+ }
+
+ minReviews, minOK := parseReviewRangeNumber(fields[i])
+ maxReviews, maxOK := parseReviewRangeNumber(fields[i+2])
+ if minOK && maxOK {
+ return minReviews, maxReviews
+ }
+ }
+
+ return 0, 0
+}
+
+func parseReviewRangeNumber(s string) (int, bool) {
+ s = strings.Trim(s, " ,.;:")
+ s = strings.NewReplacer(",", "", ".", "").Replace(s)
+
+ if n, err := strconv.Atoi(s); err == nil {
+ return n, true
+ }
+
+ words := map[string]int{
+ "one": 1,
+ "two": 2,
+ "three": 3,
+ "four": 4,
+ "five": 5,
+ "six": 6,
+ "seven": 7,
+ "eight": 8,
+ "nine": 9,
+ "ten": 10,
+ }
+ n, ok := words[s]
+
+ return n, ok
+}
+
func parseReviews(reviewsI []any) []Review {
ans := make([]Review, 0, len(reviewsI))
diff --git a/gmaps/entry_test.go b/gmaps/entry_test.go
index f709b747..a2dced02 100644
--- a/gmaps/entry_test.go
+++ b/gmaps/entry_test.go
@@ -1,6 +1,7 @@
package gmaps_test
import (
+ "encoding/json"
"fmt"
"os"
"testing"
@@ -191,6 +192,132 @@ func Test_EntryFromJSON2(t *testing.T) {
}
}
+func Test_EntryFromJSONReviewRemovalNotices(t *testing.T) {
+ jd := make([]any, 7)
+ darray := make([]any, 242)
+ darray[11] = "TÜRKITCH Köfte & Kebap"
+ darray[13] = []any{"Turkish restaurant"}
+ darray[241] = []any{
+ []any{
+ []any{
+ float64(3),
+ []any{
+ "201 to 250 reviews were removed in the past year due to defamation complaints under German law.",
+ "Reviews have been removed from this place",
+ []any{
+ []any{"https://support.google.com/contributionpolicy/answer/16997273"},
+ "More about defamation removal notices in Germany",
+ },
+ "201 to 250 reviews removed due to defamation complaints.",
+ float64(1),
+ nil,
+ float64(2),
+ "en-GB",
+ },
+ },
+ []any{
+ float64(3),
+ []any{
+ "Six to ten reviews were removed in the past year due to defamation complaints under German law.",
+ "Reviews have been removed from this place",
+ []any{
+ []any{"https://support.google.com/contributionpolicy/answer/16997273"},
+ "More about defamation removal notices in Germany",
+ },
+ "Six to ten reviews removed due to defamation complaints.",
+ float64(1),
+ nil,
+ float64(2),
+ "en-GB",
+ },
+ },
+ },
+ }
+ jd[6] = darray
+
+ raw, err := json.Marshal(jd)
+ require.NoError(t, err)
+
+ entry, err := gmaps.EntryFromJSON(raw)
+ require.NoError(t, err)
+ require.Equal(t, 201, entry.RemovedReviewsMin)
+ require.Equal(t, 250, entry.RemovedReviewsMax)
+
+ darray[241] = []any{
+ []any{
+ []any{
+ float64(3),
+ []any{
+ "Six to ten reviews were removed in the past year due to defamation complaints under German law.",
+ },
+ },
+ },
+ }
+
+ raw, err = json.Marshal(jd)
+ require.NoError(t, err)
+
+ entry, err = gmaps.EntryFromJSON(raw)
+ require.NoError(t, err)
+ require.Equal(t, 6, entry.RemovedReviewsMin)
+ require.Equal(t, 10, entry.RemovedReviewsMax)
+
+ darray[241] = []any{
+ []any{
+ []any{
+ float64(3),
+ []any{
+ "Unseren Richtlinien sind Beiträge zu dieser Art von Ort nicht zulässig.",
+ "Das Posten ist derzeit deaktiviert",
+ },
+ },
+ []any{
+ float64(3),
+ []any{
+ "Im vergangenen Jahr wurden über 250 Bewertungen aufgrund einer Beschwerde wegen Diffamierung nach deutschem Recht entfernt.",
+ "Es wurden Rezensionen zu diesem Ort entfernt",
+ []any{
+ []any{"https://support.google.com/contributionpolicy/answer/16997273"},
+ "Weitere Informationen zu Hinweisen zur Entfernung von Bewertungen wegen Diffamierungen in Deutschland",
+ },
+ "Über 250 Bewertungen aufgrund von Beschwerden wegen Diffamierung entfernt.",
+ float64(1),
+ nil,
+ float64(2),
+ "de",
+ },
+ },
+ },
+ }
+
+ raw, err = json.Marshal(jd)
+ require.NoError(t, err)
+
+ entry, err = gmaps.EntryFromJSON(raw)
+ require.NoError(t, err)
+ require.Equal(t, 251, entry.RemovedReviewsMin)
+ require.Equal(t, 0, entry.RemovedReviewsMax)
+
+ darray[241] = []any{
+ []any{
+ []any{
+ float64(3),
+ []any{
+ "One review was removed in the past year due to a defamation complaint under German law.",
+ },
+ },
+ },
+ }
+
+ raw, err = json.Marshal(jd)
+ require.NoError(t, err)
+
+ entry, err = gmaps.EntryFromJSON(raw)
+ require.NoError(t, err)
+ require.Equal(t, 1, entry.RemovedReviewsMin)
+ require.Equal(t, 1, entry.RemovedReviewsMax)
+}
+
func Test_EntryFromJSONRaw2(t *testing.T) {
raw, err := os.ReadFile("../testdata/raw2.json")
diff --git a/gmaps/multiple.go b/gmaps/multiple.go
index 46b307b7..5b490451 100644
--- a/gmaps/multiple.go
+++ b/gmaps/multiple.go
@@ -47,6 +47,7 @@ func ParseSearchResults(raw []byte) ([]*Entry, error) {
entry.ReviewRating = getNthElementAndCast[float64](business, 4, 7)
entry.ReviewCount = int(getNthElementAndCast[float64](business, 4, 8))
+ entry.RemovedReviewsMin, entry.RemovedReviewsMax = getRemovedReviewsRange(business)
fullAddress := getNthElementAndCast[[]any](business, 2)