# Changelog of Google Maps Scraper France - Leads, Phones & Emails (`badr_xba/google-maps-france`) Actor

- **URL**: https://apify.com/badr_xba/google-maps-france/changelog.md
- **Full Actor documentation**: https://apify.com/badr_xba/google-maps-france.md

## Changelog

### 2026-11-09 (review invitation)

- Successful runs that saved at least one result end their status message with one short, optional invitation to leave a Store review or open an issue (never on failed or empty runs); README section "Feedback and reviews". Output and billing unchanged.

### 2026-11-08 (visual kit 1.1.0)

#### Added

- Output fields `fid` (Google feature ID `0x…:0x…`), `kgmid` (Knowledge Graph ID `/g/…`) and `price` (price
  bracket, null when Google shows none), read at the same positions as Google Maps Scraper Pro. Existing fields unchanged.
- `FAILED_INPUTS` entries get a stable `errorCode` (`INVALID_URL`, `SHORT_LINK_EXPIRED`, `INVALID_AREA`, `NO_AREA`,
  `AREA_NOT_FOUND`, `GEOCODING_FAILED`, `PLACE_NOT_FOUND`, `BLOCKED`, `PARTIAL_BLOCKED`, `UNEXPECTED_ERROR`); the visual
  report groups failed inputs by that code.
- Visual report on kit 1.1.0: live progress page (searches done / total, places so far; at most one write a minute),
  data-quality panel, at most 3 photos per place, photo column in the table view. French labels.
- The final report is also written when the run stops on an unexpected error (status FAILED).

### 2026-11-08

#### Added

- **Place photo** `imageUrl`: Google's main photo of each place (lh3.googleusercontent.com, Google's own 408 px card
  size; a smaller thumbnail is asked at `=w408-h306-k-no` by editing only its size suffix). The search request now asks
  for the photo block the Google Maps web page asks for (`!20m…` of the `pb` parameter; before, `!20m0` = no photo).
  Null when Google has no photo for the place; Street View thumbnails, owner avatars and partner logos are never used.
  Local run 2026-11-08: 10 restaurants sur 10 à Bordeaux avec photo, 3 adresses vérifiées (HTTP 200, image/jpeg).
- Dataset schema: `imageUrl` declared, shown first (`"format": "image"`) in every view (Leads, Overview).
- Visual report: cards show the place photo (lazy, `referrerpolicy="no-referrer"`, category tile when null or broken).
- **Visual report** (`REPORT` record, Output tab → *Rapport visuel / Visual report*): business cards (category tile, name, rating, reviews, category, address, phone, website, e-mail, socials, opening hours, Google Maps link), key figures, rating
  distribution / top categories / top cities charts, search, filters (category, city, minimum rating, has website /
  phone / e-mail, open at scrape time), sorting, table view and CSV/JSON export of the filtered rows. Actor-specific labels are in French (French audience); the kit's shared buttons stay in English.
  Built by the shared `src/visual_kit.py` (v1.0.1) with this Actor's `src/report.py`. At most 2,000 rows are kept in
  memory for it; it is never billed and a report error is only logged as a warning.
- Output schema: *Visual report* link; key-value store schema: `REPORT` (text/html).

#### Tests

- `tests/test_photos.py` (8 tests): real Google answers saved 2026-11-08 with the photo block
  (`search_photos_nyc_2026-11-08.json.gz`: 20/20 photos; `search_photos_paris_2026-11-08.json.gz`: 18/20, one Street
  View-only place and one without anything stay null), old answers without the block give null, size handling, schema
  views, report cards.
- `tests/test_report.py`: 13 tests on real rows (`tests/fixtures/report_run_items.json`: 15 rows of production run EJhnTV8N9RyFZ2xws + 16 rows parsed from the saved Google answer plombier_paris.json.gz):
  image ownership / placeholder tile, invalid image links dropped, escaping, 5,000 synthetic rows (time, size, cap
  banner), JSON export = embedded data, report errors never raise, bounded buffer, and a whole offline run whose
  `REPORT` holds exactly the rows pushed.

### 2026-11-07

#### Corrigé

- `city` contenait le code postal (`75020 Paris`) : la ville est désormais seule (`Paris`), lue sur l'adresse
  structurée de Google quand elle figure dans la ligne ; le code postal reste dans `postalCode`.
- `countryCode` reprend le pays du lieu donné par Google (`FR`…), le pays saisi ne
  servant plus que de valeur de secours.
- `postalCode` et `state` se rabattent sur l'adresse structurée de Google quand la ligne d'adresse n'en donne pas.

#### Amélioré

- Sites web (e-mails, réseaux) : lecture en flux coupée à 400 Ko (un fichier énorme était d'abord lu en entier en
  mémoire), 30 s au plus par site et par itinéraire, pages de contact lues deux par deux au lieu d'une par une.
- Requêtes Google directes (sans proxy) : un seul pool de connexions au lieu d'une poignée de main TLS par page ;
  les cookies sont toujours effacés après chaque requête.
- Fiches détaillées (lieux donnés par lien ou place ID) : attente exponentielle avec part aléatoire entre les essais
  (plus longue après 403/429/5xx), pas d'attente après le dernier essai.
- Chaque ligne est vérifiée contre les types du schéma du jeu de données avant l'écriture (`src/validate.py`).
- Schéma du jeu de données : `reviewsCount` et `rank` déclarés entiers (ce que le code produit), descriptions qui
  disent quand un champ est souvent vide et pourquoi (ex. `state` vide en France).
- README : exemple de sortie corrigé (`"city": "Paris"`).
- Tests : `python -m pytest -q` (hors ligne, vraies réponses Google Maps enregistrées dans `tests/fixtures`) ;
  `tests/` exclu de l'image Docker.
