Google Places API : prospection B2B locale
Google Places API : prospection B2B locale
Pourquoi Google Places API ?
Google Maps référence des millions d'entreprises avec leurs coordonnées. L'API Google Places permet d'extraire ces données de manière structurée pour constituer une base de prospects B2B qualifiés.
Cas d'usage typiques :
- Trouver tous les restaurants d'une ville pour leur vendre un logiciel de caisse
- Lister les agences immobilières d'une région pour proposer un service photo
- Identifier les salons de coiffure pour leur proposer un outil de réservation en ligne
Configuration de l'API
1. Créer un projet Google Cloud
- Rendez-vous sur Google Cloud Console
- Créez un nouveau projet
- Activez l'API Places API (New)
- Créez une clé API dans APIs & Services > Credentials
2. Sécuriser votre clé
# Ne jamais committer votre clé dans le code !
export GOOGLE_PLACES_API_KEY="votre-clé-ici"
Restrictions recommandées :
- Restreindre par API (Places API uniquement)
- Restreindre par IP ou referrer en production
- Définir un quota journalier pour éviter les dépassements de budget
3. Tarification
| Requête | Coût (par appel) |
|---|---|
| Text Search | 0,032 $ |
| Nearby Search | 0,032 $ |
| Place Details | 0,017 $ |
| Autocomplete | 0,00283 $ |
Google offre 200 $ de crédits mensuels gratuits, ce qui permet environ 6 000 recherches Text Search par mois.
Recherche d'entreprises par type et localisation
Text Search (recommandé)
import requests
import json
API_KEY = "VOTRE_CLE_API"
BASE_URL = "https://places.googleapis.com/v1/places:searchText"
def rechercher_entreprises(requete: str, latitude: float, longitude: float, rayon_m: int = 5000) -> list[dict]:
"""
Recherche des entreprises via Google Places Text Search.
Args:
requete: ex. "restaurants italiens"
latitude: latitude du centre de recherche
longitude: longitude du centre de recherche
rayon_m: rayon de recherche en mètres (max 50000)
"""
headers = {
"Content-Type": "application/json",
"X-Goog-Api-Key": API_KEY,
"X-Goog-FieldMask": "places.displayName,places.formattedAddress,places.nationalPhoneNumber,places.websiteUri,places.googleMapsUri,places.rating,places.userRatingCount,places.businessStatus"
}
payload = {
"textQuery": requete,
"locationBias": {
"circle": {
"center": {"latitude": latitude, "longitude": longitude},
"radius": rayon_m
}
},
"languageCode": "fr"
}
response = requests.post(BASE_URL, headers=headers, json=payload)
data = response.json()
return data.get("places", [])
# Exemple : restaurants à Lyon
resultats = rechercher_entreprises("restaurants", 45.7640, 4.8357, 10000)
for place in resultats:
nom = place.get("displayName", {}).get("text", "N/A")
adresse = place.get("formattedAddress", "N/A")
tel = place.get("nationalPhoneNumber", "N/A")
site = place.get("websiteUri", "N/A")
note = place.get("rating", "N/A")
print(f"{nom} | {adresse} | {tel} | {site} | Note: {note}")
Pagination avec nextPageToken
L'API retourne 20 résultats par page. Pour en obtenir plus :
def rechercher_toutes_entreprises(requete: str, lat: float, lng: float, rayon_m: int = 5000, max_pages: int = 3) -> list[dict]:
"""Recherche paginée pour obtenir jusqu'à 60 résultats."""
tous_resultats = []
page_token = None
for page in range(max_pages):
headers = {
"Content-Type": "application/json",
"X-Goog-Api-Key": API_KEY,
"X-Goog-FieldMask": "places.displayName,places.formattedAddress,places.nationalPhoneNumber,places.websiteUri,places.rating,nextPageToken"
}
payload = {
"textQuery": requete,
"locationBias": {
"circle": {
"center": {"latitude": lat, "longitude": lng},
"radius": rayon_m
}
},
"languageCode": "fr",
"pageSize": 20
}
if page_token:
payload["pageToken"] = page_token
response = requests.post(BASE_URL, headers=headers, json=payload)
data = response.json()
resultats = data.get("places", [])
tous_resultats.extend(resultats)
page_token = data.get("nextPageToken")
if not page_token:
break
return tous_resultats
Enrichissement des données
Extraire les détails d'une entreprise
def obtenir_details(place_id: str) -> dict:
"""Obtient les détails complets d'un lieu."""
url = f"https://places.googleapis.com/v1/places/{place_id}"
headers = {
"X-Goog-Api-Key": API_KEY,
"X-Goog-FieldMask": "displayName,formattedAddress,nationalPhoneNumber,internationalPhoneNumber,websiteUri,googleMapsUri,rating,userRatingCount,businessStatus,types,regularOpeningHours"
}
response = requests.get(url, headers=headers)
return response.json()
Trouver l'email à partir du site web
Google Places ne fournit pas les emails. Il faut les extraire du site web :
import re
from bs4 import BeautifulSoup
def extraire_email_site(url: str) -> str | None:
"""Tente d'extraire un email depuis un site web."""
try:
response = requests.get(url, timeout=10, headers={
"User-Agent": "Mozilla/5.0"
})
# Chercher dans le HTML
emails = re.findall(
r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
response.text
)
# Filtrer les faux positifs courants
emails_valides = [
e for e in emails
if not e.endswith(('.png', '.jpg', '.gif', '.svg'))
and 'example' not in e
and 'wixpress' not in e
]
return emails_valides[0] if emails_valides else None
except Exception:
return None
Export vers CSV
import csv
def exporter_csv(entreprises: list[dict], fichier: str = "prospects.csv"):
"""Exporte les résultats en CSV pour import dans un CRM ou Lemlist."""
champs = ["nom", "adresse", "telephone", "site_web", "email", "note", "nb_avis"]
with open(fichier, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=champs)
writer.writeheader()
for e in entreprises:
site = e.get("websiteUri", "")
email = extraire_email_site(site) if site else ""
writer.writerow({
"nom": e.get("displayName", {}).get("text", ""),
"adresse": e.get("formattedAddress", ""),
"telephone": e.get("nationalPhoneNumber", ""),
"site_web": site,
"email": email,
"note": e.get("rating", ""),
"nb_avis": e.get("userRatingCount", "")
})
print(f"✓ {len(entreprises)} prospects exportés dans {fichier}")
Stratégie de scraping multi-villes
Pour couvrir une zone géographique large, divisez en sous-zones :
VILLES_FRANCE = [
{"nom": "Paris", "lat": 48.8566, "lng": 2.3522},
{"nom": "Lyon", "lat": 45.7640, "lng": 4.8357},
{"nom": "Marseille", "lat": 43.2965, "lng": 5.3698},
{"nom": "Toulouse", "lat": 43.6047, "lng": 1.4442},
{"nom": "Bordeaux", "lat": 44.8378, "lng": -0.5792},
{"nom": "Lille", "lat": 50.6292, "lng": 3.0573},
{"nom": "Nantes", "lat": 47.2184, "lng": -1.5536},
{"nom": "Strasbourg", "lat": 48.5734, "lng": 7.7521},
]
import time
def scraper_multi_villes(requete: str, villes: list[dict]) -> list[dict]:
"""Scrape des entreprises sur plusieurs villes."""
tous_prospects = []
for ville in villes:
print(f"Recherche à {ville['nom']}...")
resultats = rechercher_toutes_entreprises(
requete, ville["lat"], ville["lng"], rayon_m=15000
)
tous_prospects.extend(resultats)
time.sleep(1) # Respecter les rate limits
# Dédupliquer par nom + adresse
vus = set()
uniques = []
for p in tous_prospects:
cle = (
p.get("displayName", {}).get("text", ""),
p.get("formattedAddress", "")
)
if cle not in vus:
vus.add(cle)
uniques.append(p)
print(f"Total : {len(uniques)} prospects uniques sur {len(villes)} villes")
return uniques
Bonnes pratiques
- Respectez les quotas : surveillez votre consommation dans Google Cloud Console
- Cachez les résultats : stockez les résultats en base pour éviter les appels redondants
- Respectez les CGU : Google interdit le stockage massif des données Places sans affichage d'une carte
- Qualifiez vos prospects : filtrez par note, nombre d'avis et présence d'un site web
- Rate limiting : ajoutez des délais entre les requêtes (1-2 secondes)