Jak scrapować Yelp: dane biznesowe i opinie w 2026 (Public Data, PerimeterX i Proxy)

Przewodnik dla programistów: jak pobierać publiczne dane biznesowe i opinie z Yelp w 2026 — omijanie PerimeterX, proxy residential z geo-US, sesje sticky i przykłady kodu w Python oraz Node.js.

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

Ważne zastrzeżenie prawne: Ten artykuł dotyczy wyłącznie dostępu do publicznie dostępnych danych na Yelp. Przed rozpoczęciem scrapowania zapoznaj się z plikiem robots.txt Yelp oraz Regulaminem Yelp (Terms of Service). W Stanach Zjednoczonych Computer Fraud and Abuse Act (CFAA) ogranicza nieautoryzowany dostęp do systemów komputerowych — zapoznaj się z tekstem CFAA. W Unii Europejskiej RODO (GDPR) chroni dane osobowe, w tym opinie użytkowników zawierające identyfikowalne informacje. Scrapowanie treści objętych logowaniem lub naruszające regulamin platformy może być niezgodne z prawem.

Scrapowanie Yelp w 2026: co jest publicznie dostępne bez logowania

Scrapowanie Yelp — czyli pobieranie danych biznesowych Yelp — pozostaje jednym z najczęstszych zadań dla zespołów budujących zbiory danych o lokalnych firmach. Yelp udostępnia publicznie, bez logowania, znaczną część informacji biznesowych: nazwę firmy, adres, numer telefonu, kategorie, godziny otwarcia, ocenę (gwiazdki) oraz liczbę opinii. Profile firm dostępne są pod adresem /biz/<slug>, np. yelp.com/biz/torchys-tacos-austin.

Dane dostępne publicznie bez logowania obejmują:

  • Profil biznesowy: nazwa, adres, telefon, strona WWW, kategorie, godziny otwarcia, atrybuty (np. „dostępne WiFi”, „parking”).
  • Ocena i licznik opinii: ocena w gwiazdkach (1–5) oraz łączna liczba opinii.
  • Teksty opinii: pierwsze ~3–5 opinii widocznych na stronie profilu (reszta wymaga paginacji lub logowania).
  • Współrzędne geograficzne: szerokość i długość geograficzna osadzone w strukturze JSON na stronie.

Yelp oferuje również oficjalne API — Yelp Fusion API — które zwraca do 3 opinii na zapytanie dla danego biznesu. To ograniczenie sprawia, że Fusion API nie wystarcza do budowania pełnych zbiorów danych opinii, co często skłania programistów do scrapowania stron profilowych.

Kluczowa decyzja: jeśli potrzebujesz tylko podstawowych danych biznesowych (nazwa, adres, ocena, kategorie), Fusion API jest wystarczające i bezpieczniejsze. Jeśli potrzebujesz pełnych tekstów opinii lub paginacji, scrapowanie publicznych stron jest jedyną opcją — ale wymaga ostrożności i proxy.

Dlaczego scrapowanie Yelp jest trudne: PerimeterX i HUMAN

Yelp chroni swoje strony przed botami za pomocą PerimeterX (obecnie HUMAN Security) — systemu ochrony botów działającego na warstwie przeglądarki i sieci. PerimeterX stosuje kilka mechanizmów, które skutecznie blokują naивные scrapery:

Mechanizmy obronne PerimeterX

  • Cookie _px3: Token sesyjny PerimeterX generowany przez JavaScript. Scraper bez możliwości wykonania JS nie otrzyma tego ciasteczka i zostanie zablokowany już po 2–3 żądaniach z adresu IP centrum danych.
  • Wyzwanie sensor (sensor challenge): PerimeterX zbiera dane z czujników przeglądarki (canvas fingerprint, WebGL, czcionki, ruch myszy) i wysyła je w zaszyfrowanym ładunku. Brak tych danych oznacza natychmiastową blokadę.
  • Reputacja IP: Adresy IP z zakresów centrów danych (AWS, GCP, DigitalOcean) mają bardzo niską reputację. Po ~3–5 żądaniach z takiego IP, PerimeterX zwraca stronę z CAPTCHA lub błędem 403.
  • TLS fingerprinting (JA3/JA4): PerimeterX analizuje parametry handshake TLS. Biblioteki takie jak requests czy axios mają charakterystyczne odciski TLS, różne od prawdziwych przeglądarek.

W praktyce oznacza to, że prosty skrypt requests.get('https://www.yelp.com/biz/...') z adresu IP centrum danych zostanie zablokowany w ciągu pierwszych 5 żądań. To właśnie dlatego proxy residential są niezbędne do scrapowania Yelp w skali produkcyjnej.

Dlaczego proxy residential z geo-US są kluczowe

Yelp to platforma skoncentrowana głównie na rynku amerykańskim. Wyniki wyszukiwania lokalnego i sortowanie firm zależą od lokalizacji geograficznej użytkownika. Jeśli Twoje żądanie pochodzi z IP w Niemczech, Yelp może zwrócić inne wyniki lub ograniczyć dostęp do niektórych funkcji.

Adresy IP residential (pochodzące od dostawców internetu dla gospodarstw domowych) mają znacznie wyższą reputację w systemie PerimeterX niż adresy centrów danych. Rotacja adresów IP pozwala rozproszyć żądania na wiele „tożsamości”, obniżając ryzyko blokady.

Kluczowe parametry proxy dla scrapowania Yelp:

Parametr Wartość Dlaczego
Typ proxy Residential Wysoka reputacja IP w PerimeterX; niski wskaźnik blokad (<5% vs >80% dla datacenter)
Geo-targeting US (country-US) Lokalne wyniki biznesowe; zgodność z rynkiem Yelp
Rotacja Per-request lub sticky session Rotacja obniża ryzyko; sticky zachowuje sesję PerimeterX
Concurrent sessions 50–100 Równoległość bez przekraczania limitów bezpieczeństwa

Implementacja: Python — pobieranie opinii przez ProxyHat

Poniższy przykład pokazuje, jak pobrać publiczny profil biznesowy Yelp z wykorzystaniem proxy residential ProxyHat. Skrypt łączy się przez bramę HTTP gate.proxyhat.com:8080 z geo-targetingiem na Austin, TX, i sesją sticky.

import requests
import json
import re
import time
import random

# ProxyHat — residential proxy z geo-US i sticky session
proxy_url = "http://user-country-US-city-austin-session-yelp001:PASSWORD@gate.proxyhat.com:8080"

proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

# Rotacja User-Agent (prawdziwe przeglądarki)
user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
]

headers = {
    "User-Agent": random.choice(user_agents),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Connection": "keep-alive",
}

business_slug = "torchys-tacos-austin"
url = f"https://www.yelp.com/biz/{business_slug}"

try:
    response = requests.get(url, headers=headers, proxies=proxies, timeout=30)
    response.raise_for_status()

    # Yelp osadza dane w tagu script jako __INITIAL_STATE__ lub w bloku JSON
    # Szukamy wzorca "__INITIAL_STATE__" lub bizDetailsPage
    state_match = re.search(
        r'<script[^>]*>\s*window\.__INITIAL_STATE__\s*=\s*({.*?});?\s*</script>',
        response.text,
        re.DOTALL
    )

    if state_match:
        raw_json = state_match.group(1)
        # Próba parsowania (często obcięte lub zawiera nieprawidłowy JS)
        try:
            state_data = json.loads(raw_json)
            biz = state_data.get("bizDetailsPage", {}).get("businessDetails", {})
            print(f"Nazwa: {biz.get('name', 'N/A')}")
            print(f"Ocena: {biz.get('rating', 'N/A')}")
            print(f"Liczba opinii: {biz.get('reviewCount', 'N/A')}")

            reviews = biz.get("reviews", [])
            for review in reviews[:3]:
                print(f"--- Opinia ---")
                print(f"Autor: {review.get('author', {}).get('name', 'N/A')}")
                print(f"Ocena: {review.get('rating', 'N/A')}")
                text = review.get('comment', {}).get('text', '')
                print(f"Tekst: {text[:200]}...")  # obcięty tekst
        except json.JSONDecodeError:
            print("Nie udało się sparsować __INITIAL_STATE__ — spróbuj alternatywnej metody")
    else:
        print("Nie znaleziono __INITIAL_STATE__ — możliwa blokada PerimeterX")
        # Sprawdź czy odpowiedź to strona CAPTCHA
        if "px-captcha" in response.text.lower():
            print("Zablokowane przez PerimeterX CAPTCHA")

except requests.exceptions.RequestException as e:
    print(f"Błąd żądania: {e}")

# Pacing: odczekaj 2-5 sekund między żądaniami
time.sleep(random.uniform(2, 5))

Powyższy skrypt demonstruje kluczowe wzorce:

  • Geo-targeting: user-country-US-city-austin zapewnia, że wyniki są lokalizowane na Austin, TX.
  • Sesja sticky: session-yelp001 utrzymuje ten sam adres IP dla wielu żądań, co jest kluczowe, ponieważ cookie _px3 jest powiązane z IP.
  • Rotacja User-Agent: Trzy realistyczne nagłówki UA zmniejszają ryzyko wykrycia przez fingerprinting.
  • Pacing: Losowe opóźnienie 2–5 sekund między żądaniami naśladuje ludzkie zachowanie.

Pobieranie opinii przez review_feed (JSON)

Yelp ładuje dodatkowe opinie asynchronicznie przez endpoint /biz//review_feed. Ten endpoint zwraca JSON z opiniami paginowanymi po parametrze rl (review language / offset):

def fetch_reviews(business_slug, start=0, proxy_url=None):
    """Pobierz opinie przez Yelp review_feed endpoint."""
    feed_url = f"https://www.yelp.com/biz/{business_slug}/review_feed?rl={start}&sort_by=relevance_desc"
    
    proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
        "Accept": "application/json",
        "Referer": f"https://www.yelp.com/biz/{business_slug}",
        "X-Requested-With": "XMLHttpRequest",
    }
    
    resp = requests.get(feed_url, headers=headers, proxies=proxies, timeout=30)
    
    if resp.status_code == 200:
        data = resp.json()
        reviews = data.get("reviews", [])
        print(f"Pobrano {len(reviews)} opinii (offset: {start})")
        for r in reviews:
            print(f"  [{r.get('rating')}★] {r.get('user', {}).get('name', 'N/A')}: {r.get('comment', {}).get('text', '')[:100]}...")
        return reviews
    elif resp.status_code == 403:
        print("Zablokowane (403) — rotacja IP lub obniżenie tempa")
        return []
    else:
        print(f"Nieoczekiwany status: {resp.status_code}")
        return []

# Przykład paginacji
proxy = "http://user-country-US-session-yelp002:PASSWORD@gate.proxyhat.com:8080"
all_reviews = []
for offset in range(0, 100, 20):  # 5 stron po 20 opinii
    batch = fetch_reviews("torchys-tacos-austin", start=offset, proxy_url=proxy)
    if not batch:
        break
    all_reviews.extend(batch)
    time.sleep(random.uniform(3, 6))  # pacing 3-6s

Implementacja: Node.js — proxy SOCKS5 przez ProxyHat

Dla środowisk Node.js, ProxyHat obsługuje również SOCKS5 na porcie 1080. Poniższy przykład używa biblioteki socks-proxy-agent:

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

// ProxyHat SOCKS5 — residential, geo-US, sticky session
const socksUrl = 'socks5://user-country-US-session-yelp003:PASSWORD@gate.proxyhat.com:1080';
const agent = new SocksProxyAgent(socksUrl);

const userAgents = [
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
];

async function scrapeYelpBiz(slug) {
  const url = `https://www.yelp.com/biz/${slug}`;
  
  const config = {
    url,
    httpsAgent: agent,
    timeout: 30000,
    headers: {
      'User-Agent': userAgents[Math.floor(Math.random() * userAgents.length)],
      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
      'Accept-Language': 'en-US,en;q=0.9',
      'Accept-Encoding': 'gzip, deflate, br',
    },
  };

  try {
    const response = await axios(config);
    
    // Szukaj __INITIAL_STATE__ w HTML
    const match = response.data.match(
      /window\.__INITIAL_STATE__\s*=\s*({[\s\S]*?});?\s*<\/script>/
    );
    
    if (match) {
      const rawJson = match[1];
      try {
        const state = JSON.parse(rawJson);
        const biz = state?.bizDetailsPage?.businessDetails || {};
        console.log(`Nazwa: ${biz.name || 'N/A'}`);
        console.log(`Ocena: ${biz.rating || 'N/A'}`);
        console.log(`Liczba opinii: ${biz.reviewCount || 'N/A'}`);
        console.log(`Telefon: ${biz.phone || 'N/A'}`);
        console.log(`Adres: ${JSON.stringify(biz.location || {})}`);
        
        const reviews = biz.reviews || [];
        reviews.slice(0, 3).forEach((r, i) => {
          console.log(`\n--- Opinia ${i + 1} ---`);
          console.log(`Autor: ${r?.author?.name || 'N/A'}`);
          console.log(`Ocena: ${r?.rating || 'N/A'}`);
          console.log(`Tekst: ${(r?.comment?.text || '').substring(0, 150)}...`);
        });
      } catch (e) {
        console.error('Błąd parsowania JSON:', e.message);
      }
    } else {
      console.log('Nie znaleziono __INITIAL_STATE__');
      if (response.data.toLowerCase().includes('px-captcha')) {
        console.log('Zablokowane przez PerimeterX CAPTCHA');
      }
    }
  } catch (error) {
    console.error('Błąd żądania:', error.message);
  }
}

// Uruchom z pacingiem
(async () => {
  await scrapeYelpBiz('torchys-tacos-austin');
  await new Promise(r => setTimeout(r, 3000 + Math.random() * 2000));
})();

Paginacja, rate-limit pacing i sesje sticky

Skuteczny scraper Yelp musi balansować między szybkością a niezawodnością. Oto kluczowe strategie produkcyjne:

Paginacja opinii

Endpoint review_feed zwraca ~20 opinii na żądanie. Parametr rl kontroluje offset. Paginacja powinna być stopniowa — nagły skok z offsetu 0 do 200 wygląda podejrzanie dla PerimeterX.

Rate-limit pacing

Rekomendowane tempo: 1 żądanie na 3–6 sekund na sesję IP. Przy 50 równoległych sesjach sticky osiągasz ~10 żądań/sekundę, co pozwala pobrać ~36 000 opinii na godzinę. Wyższe tempo znacząco zwiększa ryzyko blokad.

Sesje sticky — kiedy i dlaczego

Cookie _px3 PerimeterX jest powiązane z adresem IP. Jeśli zmienisz IP w środku sesji, PerimeterX uzna to za podejrzane i może zażądać CAPTCHA. Dlatego:

  • Używaj sticky session (-session-abc123) dla wszystkich żądań do jednej firmy (profil + paginacja opinii).
  • Zmieniaj sesję (nowy identyfikator) przy przejściu do kolejnej firmy.
  • Utrzymuj jedną sesję przez maksymalnie 10–15 minut lub ~50 żądań — potem rotuj.

Rotacja User-Agent i nagłówków

Używaj zestawu realistycznych, aktualnych User-Agentów (Chrome, Firefox, Safari — wersje z ostatnich 6 miesięcy). Nie używaj przestarzałych stringów jak Python/3.9 czy Mozilla/5.0 (compatible; bot/1.0). Utrzymuj spójność: jeśli UA mówi Chrome na Windows, nagłówki Accept i Sec-CH-UA powinny być zgodne.

ProxyHat: konfiguracja i integracja

ProxyHat oferuje proxy residential, mobile i datacenter z globalnym geo-targetingiem. Dla scrapowania Yelp rekomendowane są proxy residential z geo-US. Konfiguracja jest prosta — wszystkie parametry (kraj, miasto, sesja) przekazywane są w nazwie użytkownika.

Formaty połączenia ProxyHat:

Protokół URL Port
HTTP http://user-country-US:PASSWORD@gate.proxyhat.com:8080 8080
SOCKS5 socks5://user-country-US:PASSWORD@gate.proxyhat.com:1080 1080
HTTP + city http://user-country-US-city-austin:PASSWORD@gate.proxyhat.com:8080 8080
HTTP + session http://user-session-abc123:PASSWORD@gate.proxyhat.com:8080 8080

Sprawdź ceny ProxyHat oraz dostępne lokalizacje. Więcej przykładów integracji znajdziesz w dokumentacji ProxyHat.

Etyczne scrapowanie i kiedy używać oficjalnego API

Scrapowanie Yelp powinno być zawsze przeprowadzane z poszanowaniem prawnej i etycznym. Oto zasady, których należy przestrzegać:

  1. Szanuj robots.txt: Sprawdź yelp.com/robots.txt przed rozpoczęciem. Jeśli dana ścieżka jest wykluczona, nie scrapuj jej.
  2. Nie pobieraj danych osobowych: Opinie Yelp zawierają nazwy użytkowników, zdjęcia profilowe i teksty, które mogą zawierać dane osobowe. Zgodnie z RODO (GDPR), przetwarzanie tych danych wymaga podstawy prawnej. Jeśli budujesz zbiór danych do analizy rynkowej, rozważ anonimizację (usunięcie nazw użytkowników, hashowanie identyfikatorów).
  3. Nie omijaj logowania: Jeśli treść jest dostępna tylko po zalogowaniu, nie używaj sztucznych kont ani nie omijaj mechanizmów autoryzacji. To może naruszać CFAA.
  4. Preferuj Fusion API tam, gdzie wystarcza: Jeśli potrzebujesz tylko podstawowych danych biznesowych (nazwa, adres, ocena, kategorie, telefon), Yelp Fusion API jest bezpieczniejszą i legalną opcją. Fusion API zwraca do 3 opinii na biznes — jeśli to wystarcza, nie scrapuj.
  5. Ogranicz tempo: Nie przeciążaj serwerów Yelp. Tempo 1 żądania/3–6 sekund na sesję jest rozsądne. Agresywne scrapowanie może zostać uznane za atak DoS.
  6. Używaj danych odpowiedzialnie: Nie redistribuuj pobranych danych bez licencji. Yelp posiada prawa autorskie do swoich treści.

Złota zasada: jeśli Fusion API pokrywa 80% Twoich potrzeb — używaj go. Scrapuj tylko te 20%, których API nie dostarcza (pełne teksty opinii, paginacja, dane osadzone w HTML).

Najczęstsze błędy i pułapki

  • Używanie proxy datacenter — PerimeterX blokuje adresy centrów danych po 3–5 żądaniach. Wskaźnik sukcesu spada poniżej 20%.
  • Brak sesji sticky — Zmiana IP w środku sesji powoduje utratę cookie _px3 i natychmiastową blokadę.
  • Zbyt szybkie tempo — 10 żądań/sekundę z jednego IP = gwarantowana blokada. Trzymaj 1 żądanie/3–6s.
  • Ignorowanie TLS fingerprinting — Biblioteka requests ma charakterystyczny JA3. Rozważ użycie curl_cffi lub tls-client do naśladowania przeglądarki.
  • Parsing HTML z regex — Struktura __INITIAL_STATE__ może się zmieniać. Używaj odpornego parsowania JSON z fallbackiem.

Kluczowe wnioski (Key Takeaways)

  • Yelp udostępnia publicznie dane biznesowe (nazwa, adres, ocena, ~3 opinii) bez logowania — reszta wymaga scrapowania lub Fusion API (limit 3 opinii).
  • PerimeterX/HUMAN blokuje scrapery z IP centrów danych po 3–5 żądaniach — proxy residential są niezbędne.
  • Geo-targeting US (country-US) zapewnia lokalne wyniki biznesowe i wyższą reputację IP.
  • Sesje sticky utrzymują ciągłość cookie _px3 — zmieniaj sesję przy przejściu do nowej firmy.
  • Tempo 1 żądanie/3–6 sekund na sesję + 50 równoległych sesji = ~36 000 opinii/godzinę z wskaźnikiem sukcesu >90%.
  • Preferuj Fusion API, gdy wystarcza 3 opinii na biznes — to legalna i stabilna opcja.

Gotowy na budowę zbioru danych Yelp? Zacznij od przypadku użycia web scraping i planów cenowych ProxyHat. Jeśli potrzebujesz monitorowania wyników wyszukiwania, sprawdź również SERP tracking.

Gotowy, aby zacząć?

Dostęp do ponad 50 mln rezydencjalnych IP w ponad 148 krajach z filtrowaniem AI.

Zobacz cenyProxy rezydencjalne
← Powrót do Bloga