Comment scraper les annonces et avis d'entreprises Yelp en 2026

Guide développeur pour scraper Yelp en 2026 : données publiques, contournement de PerimeterX/HUMAN, proxies résidentiels US, snippets Python et Node.js, et bonnes pratiques éthiques.

How to Scrape Yelp Business Listings and Reviews in 2026: Public Data, PerimeterX, and Production Patterns

Comment scraper Yelp en 2026 : ce qu'il faut savoir avant la première requête

Scraper Yelp reste l'un des cas d'usage les plus demandés pour les équipes qui construisent des jeux de données d'entreprises locales : notations, catégories, horaires, et texte d'avis. Mais la plateforme a considérablement durci ses défenses anti-bot en 2024-2025, et la frontière entre données publiques accessibles sans connexion et contenu protégé par des conditions d'utilisation est devenue floue. Ce guide explique ce qui est réellement accessible, comment PerimeterX (désormais HUMAN) filtre le trafic automatisé, et comment configurer des proxies résidentiels rotatifs avec ProxyHat pour maintenir un taux de succès acceptable.

⚠️ Caveau légal. Avant toute collecte, lisez les Conditions d'utilisation de Yelp, le fichier robots.txt du domaine, et évaluez votre exposition au Computer Fraud and Abuse Act (CFAA) aux États-Unis et au RGPD dans l'UE. Les avis contiennent souvent des données personnelles (noms, photos, localisation) — leur traitement nécessite une base légale au sens de l'article 6 du RGPD. Ce guide ne couvre que l'accès aux données publiques sans connexion ; ne scrapez jamais de contenu derrière authentification.

Si votre besoin se limite à trois avis par établissement et à des données structurées, l'API officielle Yelp Fusion est plus fiable, plus rapide et juridiquement plus sûr que le scraping. Le scraping n'a du sens que lorsque vous avez besoin de l'intégralité du flux d'avis, de données historiques, ou d'une couverture au-delà des limites de l'API.

Ce qui est publiquement accessible sur Yelp sans connexion

Une page d'établissement Yelp (/biz/<slug>) affiche sans connexion :

  • Identité : nom, adresse, téléphone, site web, catégorie(s), attributs (parking, terrasse, wifi).
  • Notation : note globale sur 5, nombre d'avis, répartition par étoiles.
  • Horaires : horaires d'ouverture hebdomadaires et statut actuel (ouvert/fermé).
  • Avis : les premiers avis (généralement 8-10) avec texte, note, date, nom d'auteur, photo de profil.
  • Photos : galerie publique avec légendes et attributions.

En revanche, ne sont pas accessibles sans connexion :

  • La pagination complète des avis au-delà de la première page (Yelp charge les avis suivants via XHR, et le endpoint /review_feed requiert un cookie _px3 valide).
  • Les messages privés, les offres promotionnelles et les événements réservés aux utilisateurs connectés.
  • Les données analytiques du propriétaire (insights, statistiques de trafic).

La Fusion API impose une limite stricte de 3 avis maximum par établissement et un quota de 5 000 appels/jour par clé. Pour un projet d'analyse de sentiment sur 10 000 restaurants avec 50 avis chacun, l'API ne suffit pas — d'où le recours au scraping.

Les défenses anti-bot de Yelp : PerimeterX / HUMAN

Yelp protège son trafic avec PerimeterX (HUMAN), un bot-defender de nouvelle génération qui combine plusieurs signaux :

Le cookie _px3 et le sensor challenge

PerimeterX injecte un cookie _px3 généré côté navigateur par un script JavaScript propriétaire. Ce script exécute un sensor challenge : il collecte des centaines de signaux (mouvements de souris, timing des frappes, propriétés du canvas WebGL, fingerprint du navigateur) et les encode dans une payload chiffrée. Sans ce cookie valide, toute requête vers /review_feed ou une page d'établissement est rejetée avec un HTTP 403 ou redirigée vers une page CAPTCHA.

Empreinte TLS (TLS fingerprinting)

PerimeterX inspecte aussi l'empreinte TLS de la connexion (JA3/JA4). Les bibliothèques HTTP standard comme requests ou node-fetch produisent une empreinte TLS différente de Chrome ou Firefox — un signal immédiat de bot. Pour réduire ce risque, des outils comme curl-impersonate ou curl_cffi en Python imitent l'empreinte TLS de Chrome avec une précision suffisante pour passer le filtre initial.

CAPTCHA après quelques requêtes datacenter

En pratique, les IPs datacenter (AWS, GCP, OVH, Hetzner) sont identifiées par leur plage ASN et reçoivent un score de réputation IP très bas. Après 3 à 10 requêtes depuis une IP datacenter, PerimeterX déclenche un CAPTCHA (généralement un challenge Arkose Labs ou hCaptcha). Les proxies résidentiels, en revanche, bénéficient du score de réputation d'un FAI domestique — ils passent le filtre IP initial, laissant le sensor challenge comme seul obstacle.

Pourquoi des proxies résidentiels rotatifs avec géo US

Yelp est une plateforme centrée sur le commerce local : les résultats de recherche et les recommandations dépendent fortement de la géolocalisation de l'IP. Une requête depuis une IP française sur yelp.com/search retourne des résultats limités ou des redirections vers yelp.fr. Pour scraper des établissements aux États-Unis, vous devez :

  1. Utiliser une IP résidentielle US — pas seulement pour la réputation anti-bot, mais pour obtenir les bons résultats locaux.
  2. Cibler une ville précise quand c'est pertinent — -country-US-city-austin retourne des résultats d'Austin, ce qui est cohérent avec le slug /biz/house-of-curry-austin.
  3. Rotation par requête pour répartir la charge et éviter qu'une seule IP reçoive trop de requêtes en peu de temps.

Avec ProxyHat, le géo-ciblage se fait directement dans le username :

http://user-country-US-city-austin:PASSWORD@gate.proxyhat.com:8080

Pour une session persistante (sticky session) qui garde la même IP pendant tout le scraping d'un établissement — nécessaire pour conserver le cookie _px3 valide — ajoutez un identifiant de session :

http://user-country-US-city-austin-session-biz001:PASSWORD@gate.proxyhat.com:8080

Snippet Python : extraire __INITIAL_STATE__ via ProxyHat

Yelp embarque un objet JSON volumineux dans window.__INITIAL_STATE__ au sein du HTML de la page d'établissement. Ce blob contient les premiers avis, les informations de l'établissement, et les métadonnées de pagination. L'extraire évite d'avoir à résoudre le sensor challenge de /review_feed pour la première page.

import requests
import json
import re
import random
import time

PROXY = "http://user-country-US-city-austin-session-biz001:PASSWORD@gate.proxyhat.com:8080"
proxies = {"http": PROXY, "https": PROXY}

UA_POOL = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
    "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
    "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15",
]

def fetch_biz(slug):
    url = f"https://www.yelp.com/biz/{slug}"
    headers = {
        "User-Agent": random.choice(UA_POOL),
        "Accept-Language": "en-US,en;q=0.9",
        "Accept": "text/html,application/xhtml+xml",
    }
    r = requests.get(url, headers=headers, proxies=proxies, timeout=30)
    r.raise_for_status()

    # Extract embedded __INITIAL_STATE__ JSON blob
    m = re.search(
        r'window\.__INITIAL_STATE__\s*=\s*(\{.*?\});',
        r.text,
        re.DOTALL
    )
    if not m:
        raise RuntimeError("Block page or CAPTCHA — rotate proxy/session")

    state = json.loads(m.group(1))
    reviews = (
        state.get("bizDetailsPage", {})
             .get("reviewFeed", {})
             .get("reviews", [])
    )

    for rv in reviews[:1]:
        print(json.dumps({
            "id": rv.get("id"),
            "rating": rv.get("rating"),
            "text": rv.get("comment", {}).get("text", "")[:120],
            "author": rv.get("author", {}).get("displayName"),
            "date": rv.get("publishedDate"),
        }, indent=2, ensure_ascii=False))

    return reviews

if __name__ == "__main__":
    fetch_biz("house-of-curry-austin")
    time.sleep(3)  # pace between businesses

Sortie attendue (objet tronqué) :

{
  "id": "abc123XY",
  "rating": 5,
  "text": "Best biryani in Austin, hands down. The lamb was tender and the rice was fragrant...",
  "author": "Priya M.",
  "date": "2025-11-14T03:22:00Z"
}

Points clés : le -session-biz001 garde la même IP résidentielle pour toutes les requêtes d'un même établissement, ce qui maintient la validité du cookie _px3. Changez de session pour l'établissement suivant (-session-biz002) pour obtenir une nouvelle IP et répartir la charge.

Snippet Node.js : scraping via SOCKS5 (port 1080)

Pour les pipelines JavaScript/TypeScript, ProxyHat expose aussi un endpoint SOCKS5 sur le port 1080. SOCKS5 est utile quand vous voulez un tunnel TCP brut sans overhead HTTP CONNECT.

const { SocksProxyAgent } = require('socks-proxy-agent');
const fetch = require('node-fetch');

const agent = new SocksProxyAgent(
  'socks5://user-country-US-session-rev42:PASSWORD@gate.proxyhat.com:1080'
);

async function fetchReviews(slug) {
  const url = `https://www.yelp.com/biz/${slug}`;
  const res = await fetch(url, {
    agent,
    headers: {
      'User-Agent':
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' +
        'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
      'Accept-Language': 'en-US,en;q=0.9',
    },
    timeout: 30000,
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const html = await res.text();
  const m = html.match(/window\.__INITIAL_STATE__\s*=\s*(\{[\s\S]*?\});/);
  if (!m) throw new Error('CAPTCHA or block — rotate session');
  const state = JSON.parse(m[1]);
  const reviews = state.bizDetailsPage?.reviewFeed?.reviews ?? [];
  console.log(JSON.stringify(reviews[0], null, 2).slice(0, 500));
  return reviews;
}

fetchReviews('house-of-curry-austin').catch(console.error);

Pagination, rate-limit, sessions sticky et rotation d'user-agents

Pagination des avis

Les avis au-delà de la première page sont chargés via XHR vers /review_feed, qui nécessite un cookie _px3 valide. Deux stratégies :

  • Stratégie A (simple) : scrapez uniquement la première page de chaque établissement. Pour 10 000 établissements, cela donne déjà ~80 000 avis (8-10 par page), ce qui suffit à beaucoup de projets d'analyse de sentiment.
  • Stratégie B (complète) : utilisez une session sticky par établissement, résolvez le sensor challenge avec un navigateur headless (Playwright/Puppeteer) pour obtenir _px3, puis réutilisez ce cookie pour les requêtes XHR vers /review_feed avec ?start=20, ?start=40, etc.

Rate-limit pacing

Yelp n'a pas de rate-limit HTTP explicite documenté, mais PerimeterX déclenche un CAPTCHA après un certain nombre de requêtes par IP dans une fenêtre de temps. En pratique, un rythme de 1 requête toutes les 3-5 secondes par session est raisonnable. Avec 100 sessions concurrentes (100 IPs résidentielles), cela donne ~20-30 requêtes/seconde — largement suffisant pour la plupart des projets.

StratégieRequêtes/secLatence médianeRisque de blocage
IP datacenter, sans proxy~0200msBlocage immédiat (3-10 req)
Résidentiel rotatif, 1 req/3s~0,33/IP800msFaible
Résidentiel rotatif, 1 req/1s~1/IP800msMoyen
Résidentiel + sticky session, 1 req/5s~0,2/IP600msTrès faible

Sticky sessions et continuité

Le paramètre -session-XXX dans le username ProxyHat garantit que toutes les requêtes d'une même session sortent de la même IP résidentielle. Cela est crucial pour :

  • Maintenir la validité du cookie _px3 (lié à l'IP).
  • Conserver la cohérence géographique des résultats.
  • Éviter que PerimeterX détecte un changement d'IP en milieu de session.

Pattern recommandé : une session par établissement, rotation entre établissements.

Rotation d'user-agents

La rotation d'user-agents seule ne suffit pas à tromper PerimeterX (le sensor challenge va plus loin), mais un UA cohérent avec votre empreinte TLS est nécessaire. Si vous utilisez requests, utilisez un UA Chrome récent. Si vous utilisez curl_cffi avec impersonate="chrome124", l'UA est géré automatiquement et l'empreinte TLS correspond.

Erreurs courantes et edge cases

  • Parser __INITIAL_STATE__ avec un regex trop gourmand : le blob JSON peut dépasser 500 KB. Utilisez un regex non-gourmand (.*?) ou un parser JSON streaming.
  • Ignorer le robots.txt : Yelp interdit le scraping de plusieurs chemins dans son robots.txt. Respectez-le.
  • Stocker des données personnelles sans base légale : les noms d'auteurs d'avis sont des données personnelles au sens du RGPD. Anonymisez-les ou obtenez un consentement.
  • Utiliser un datacenter proxy "pas cher" : les IPs datacenter sont immédiatement flaggées par PerimeterX. C'est un gaspillage de budget.
  • Ne pas gérer les redirections 301 : Yelp redirige parfois vers une URL canonique différente. Laissez requests suivre les redirections (allow_redirects=True par défaut).
  • Oublier le header Accept-Language : sans en-US, Yelp peut servir une version localisée avec une structure HTML différente.

Configuration ProxyHat pour Yelp

ProxyHat propose des proxies résidentiels rotatifs avec géo-ciblage au niveau pays et ville. Pour le scraping Yelp, la configuration recommandée est :

  • Type : résidentiel rotatif (pour la réputation IP).
  • Géo : -country-US (ou -country-US-city-austin pour un ciblage ville).
  • Session : sticky par établissement (-session-biz001).
  • Port : 8080 (HTTP) ou 1080 (SOCKS5).
  • Gateway : gate.proxyhat.com.

Consultez la page tarifs pour les plans disponibles, et la page localisations pour la couverture géographique. Pour des cas d'usage avancés comme le SERP tracking ou le web scraping à grande échelle, consultez nos guides dédiés. La documentation technique complète est disponible sur docs.proxyhat.com.

Éthique et quand utiliser l'API officielle

Le scraping n'est pas illégal par défaut, mais il n'est pas non plus neutre. Avant de scraper Yelp, posez-vous ces questions :

  1. Pouvez-vous obtenir ces données via l'API Fusion ? Si 3 avis par établissement suffisent, l'API est plus rapide, plus stable et juridiquement plus sûr.
  2. Respectez-vous le robots.txt ? C'est un standard de facto reconnu par les tribunaux américains dans certaines jurisprudences.
  3. Les données collectées contiennent-elles des informations personnelles ? Si oui, le RGPD (UE) et le CCPA (Californie) s'appliquent. Anonymisez ou pseudonymisez.
  4. Allez-vous publier ou revendre les données ? Yelp interdit explicitement la republication de ses données dans ses conditions d'utilisation.
  5. Avez-vous un besoin légitime et proportionné ? Le scraping pour la recherche académique ou l'analyse de marché est plus défendable que le scraping pour construire un concurrent direct.

En résumé : si l'API Fusion couvre votre besoin, utilisez-la. Si elle ne suffit pas, scrapez avec mesure, respectez le robots.txt, limitez les données personnelles, et documentez votre base légale. Le scraping éthique est un marathon, pas un sprint.

Points clés à retenir

  • Les pages /biz/<slug> sont accessibles sans connexion et contiennent ~8-10 avis dans __INITIAL_STATE__.
  • L'API Fusion limite à 3 avis par établissement et 5 000 appels/jour — insuffisant pour les projets à grande échelle.
  • PerimeterX (HUMAN) bloque les IPs datacenter après 3-10 requêtes ; les proxies résidentiels US sont indispensables.
  • Utilisez -session-XXX pour les sticky sessions (une par établissement) et -country-US-city-XXX pour le géo-ciblage.
  • Pacez à 1 req/3-5s par session, rotez les user-agents, et gérez les échecs avec retry + changement de session.
  • Respectez le robots.txt, le RGPD et le CFAA. Préférez l'API Fusion quand elle suffit.

Prêt à commencer ?

Accédez à plus de 50M d'IPs résidentielles dans plus de 148 pays avec filtrage IA.

Voir les tarifsProxies résidentiels
← Retour au Blog