Yelp zawiera 330 milionów łącznych recenzji na 8,4 miliona aktywnych profili firm — a wyciągnięcie tych danych do wygodnego formatu jeszcze nigdy nie było tak trudne. W latach 2024–2025 Yelp mocno zaostrzył walkę z botami, przez co większość dotychczasowych poradników o scrapowaniu w Pythonie po prostu cicho przestała działać.
Jeśli ostatnio próbowałeś uruchomić scraper do Yelp i wpadłeś w serię błędów 403, pustych odpowiedzi HTML albo CAPTCHA, których pół roku temu jeszcze nie było, to nie wymysł. Yelp dziś używa fingerprintingu TLS/JA3, rotujących i zaciemnionych nazw klas CSS oraz agresywnej oceny reputacji IP — co oznacza, że stary sposób requests + BeautifulSoup, nadal polecany w większości poradników, wywraca się już przy pierwszym żądaniu. Spędziłem tygodnie na testowaniu różnych podejść pod aktualne zabezpieczenia Yelp i ten przewodnik obejmuje wszystko, co naprawdę działa w 2025 roku: oficjalne Fusion API (i dlaczego prawdopodobnie nie wystarczy), kompletny workflow scrapowania w Pythonie z wielowarstwową strategią antyblokową oraz 2‑klikową alternatywę no-code z Thunderbit dla osób, które po prostu chcą danych bez maratonu debugowania.
Wypróbuj Thunderbit do scrapowania Yelp
Po co scrapować Yelp w Pythonie i kto naprawdę na tym korzysta
Zanim napiszesz choćby jedną linię kodu, warto odpowiedzieć sobie na pytanie: jaki właściwie jest biznesowy sens danych z Yelp? Ta platforma to nie tylko serwis z recenzjami restauracji — to w praktyce żywa baza lokalnych firm, zawierająca uporządkowane dane kontaktowe, oceny, kategorie, godziny otwarcia i setki milionów opinii klientów.

Oto, kto korzysta na tym najbardziej i jakie dane najczęściej pobiera:
| Przypadek użycia | Najważniejsze pola danych | Dlaczego to ma znaczenie |
|---|---|---|
| Sprzedaż i lead generation | nazwa firmy, telefon, strona, adres, kategoria, ocena | Budowanie precyzyjnych list potencjalnych klientów z sektora lokalnych MŚP — 4 na 5 użytkowników Yelp jest gotowych do zakupu już po wejściu na platformę |
| Analiza konkurencji | recenzje, oceny gwiazdkowe, liczba opinii, sentyment | Monitorowanie reputacji konkurentów, wykrywanie braków w obsłudze, śledzenie trendów |
| Badania rynku i NLP | pełny tekst recenzji, daty, metadane recenzentów | Analiza sentymentu, modelowanie tematów — recenzje Yelp są jednym z najczęściej używanych korpusów NLP w badaniach akademickich |
| Nieruchomości i wybór lokalizacji | zagęszczenie firm, mix kategorii, jakość recenzji według obszaru | Wybór lokalizacji dla sieci i retailu — Yelp sprzedaje Location Intelligence jako licencjonowany produkt B2B dokładnie do tego celu |
| E-commerce i operacje | sygnały cenowe, skargi klientów, godziny obsługi | Monitorowanie, jak oceniani są konkurenci, wykrywanie wzorców operacyjnych |
Wspólny mianownik jest prosty: prawdziwym celem są dane strukturalne, a Python jest tylko jednym ze sposobów dotarcia do nich. Część czytelników będzie chciała pełnej kontroli programistycznej. Inni po prostu potrzebują arkusza z kontaktami do hydraulików w Austin. Oba scenariusze opisuję poniżej.
Yelp Fusion API vs scrapowanie w Pythonie: co wybrać?
Większość poradników w ogóle pomija ten wybór i od razu przechodzi do kodu, nie sprawdzając, czy oficjalne Yelp Fusion API (dziś przemianowane na „Yelp Places API”) nie wystarczyłoby do celu. Z mojego doświadczenia taka analiza oszczędza godziny zmarnowanej pracy — bo API jest świetne do jednych rzeczy, a zupełnie bezużyteczne do innych.
Co naprawdę daje Fusion API
Fusion API udostępnia uporządkowane wyszukiwanie firm, szczegóły biznesowe, autouzupełnianie i endpoint z recenzjami. Jest oficjalne, dobrze opisane i nie wymaga obchodzenia zabezpieczeń antybotowych.
Problem zaczyna się przy endpointcie z recenzjami. Oto, co potwierdzili pracownicy Yelp na GitHubie:
„Yelp API nie zwraca pełnego tekstu recenzji. Domyślnie udostępniane są trzy fragmenty recenzji po 160 znaków.” — odpowiedź pracownika Yelp, GitHub Issue #163
To nie błąd — to założenie projektu. API fizycznie ogranicza się do 3 fragmentów recenzji (7 w planie Premium), każdy ucięty do około 160 znaków. Brakuje metadanych recenzji (głosów useful/funny/cool), historii recenzenta i odpowiedzi właściciela. Do tego dzienny limit dla nowych klientów spadł po maju 2023 do 300–500 wywołań dziennie — wcześniej było to 5 000. Cennik startuje od $29/miesiąc.
Ramy decyzyjne
| Czynnik | Yelp Fusion API | Scrapowanie w Pythonie | Thunderbit (no-code) |
|---|---|---|---|
| Pełne recenzje | ❌ Tylko 3 fragmenty (~160 znaków każdy) | ✅ Wszystkie recenzje przez GraphQL | ✅ Wszystkie widoczne recenzje |
| Limity zapytań | 300–500/dzień (nowi); 5 000 (legacy) | Zarządzane samodzielnie (koszt proxy) | Model kredytowy |
| Czas konfiguracji | ~15 min (klucz API + SDK) | Od kilku godzin do kilku dni | ~2 minuty |
| Pola biznesowe | ~20 pól strukturalnych | Nielimitowane (HTML/JSON) | Pola sugerowane przez AI |
| Obsługa antybota | N/D (oficjalne) | Trzeba zbudować samemu | Obsługiwane automatycznie |
| Ryzyko prawne | ✅ Oficjalne | ⚠️ Szara strefa ToS | ⚠️ Jak w przypadku scrapowania |
| Koszt | minimum $29/mies. | Darmowe (+ proxy $0.75–$4/GB) | Dostępny darmowy plan |
| Utrzymanie | Niskie (API stabilne) | Wysokie (selektory się psują, antybot się zaostrza) | Niskie (AI dostosowuje się ponownie) |
Wybierz Fusion API, jeśli: potrzebujesz podstawowych informacji o firmie, małych jednorazowych lookupów albo oficjalnej integracji — i wystarczą Ci 3 fragmenty recenzji na firmę.
Wybierz scrapowanie w Pythonie, jeśli: potrzebujesz pełnego tekstu recenzji, wszystkich opinii dla danej firmy, metadanych recenzji, więcej niż 240 wyników na wyszukiwanie albo budżet masz niższy niż $29/miesiąc.
Wybierz Thunderbit, jeśli: chcesz szybko dostać dane bez pisania i utrzymywania kodu. O tym więcej w sekcji no-code poniżej.
No-code shortcut: scrapowanie Yelp z Thunderbit (bez Pythona)
Zanim przejdziemy do głębokiego nurkowania w Pythonie, oto najszybsza ścieżka dla osób, których celem są dane, a nie ćwiczenie programistyczne. Każdy konkurencyjny poradnik zakłada znajomość Pythona, ale w mojej pracy w Thunderbit widzę, że ogromna część osób wpisujących „scrape Yelp” to handlowcy, menedżerowie operacyjni i właściciele małych firm, którzy po prostu chcą arkusz z lokalnymi biznesami — a nie kursu fingerprintingu TLS.
Thunderbit ma już gotowe szablony dla Yelp:
- Yelp Business Web Scraper — pobiera nazwę firmy, ocenę, dane kontaktowe, adres, godziny, kategorię
- Yelp Review Scraper — pobiera nazwę użytkownika recenzenta, treść recenzji, ocenę, datę i lokalizację recenzenta
Jak to działa w praktyce
- Otwórz stronę wyników wyszukiwania Yelp albo stronę firmy w Chrome
- Kliknij AI Suggest Fields w rozszerzeniu Thunderbit — AI odczyta stronę i zaproponuje kolumny (nazwa firmy, ocena, liczba recenzji, przedział cenowy, kategoria, adres, telefon, URL)
- Kliknij Scrape — gotowe
W przypadku gotowych szablonów Yelp jest jeszcze prościej: otwierasz szablon i klikasz Scrape.
Scrapowanie podstron automatycznie obsługuje etap wzbogacania danych — zaczynasz od strony wyników Yelp, włączasz scraping podstron, a Thunderbit odwiedza każdą stronę firmy, aby pobrać godziny, pełne recenzje, stronę internetową, zdjęcia i udogodnienia. Bez dodatkowej konfiguracji.
Paginacja działa automatycznie — zarówno przy klikaniu, jak i przewijaniu, bez dodatkowych ustawień. (Więcej o tym, jak to działa, znajdziesz w naszym przewodniku po paginacji.)
Eksport jest darmowy na każdym planie — Excel, Google Sheets, Airtable, Notion, CSV, JSON. Bez pandas, bez kodu do zapisu CSV.
Porównanie czasu
| Czas | Scraper w Pythonie | Thunderbit |
|---|---|---|
| Pierwsze uruchomienie | Od kilku godzin do kilku dni (selektory, paginacja, proxy, retry) | Około 30 sekund z gotowym szablonem Yelp |
| Gdy Yelp zmienia markup | Ręczne przepisywanie selektorów | Ponowne kliknięcie AI Suggest Fields — automatyczne dostosowanie |
| Gdy IP zostanie zablokowane | Debugowanie, rotacja pul proxy, ponowne testy | Tryb cloud obsługuje rotację IP |
| Eksport do Google Sheets | Napisać OAuth i kod łączący z pandas | Jedno kliknięcie, za darmo |
Jeśli przetestujesz Thunderbit najpierw i uznasz, że wystarcza, możesz pominąć resztę artykułu. Jeśli potrzebujesz pełnej kontroli programistycznej, własnych pól lub skali większej niż kilka tysięcy rekordów miesięcznie — czytaj dalej.
Biblioteki Pythona do scrapowania Yelp: którą wybrać?
„Czy użyć Scrapy, BS4+requests czy Selenium?” to jedno z najczęstszych pytań w wątkach r/webscraping dotyczących Yelp. A jednak każdy poradnik po prostu wybiera ulubioną bibliotekę i idzie dalej, bez wyjaśnienia dlaczego. Oto uczciwy podział.
Rzeczywistość 2025: requests + BeautifulSoup nie działa na Yelp
Zestaw, który poleca każdy klasyczny poradnik do Yelp — pip install requests beautifulsoup4 — zostaje zablokowany przy pierwszym żądaniu w 2025 roku. Nie przy pięćdziesiątym. Przy pierwszym.
Powód: biblioteka Pythona requests wysyła fingerprint TLS/JA3, który nie odpowiada żadnej prawdziwej przeglądarce. Warstwa antybota Yelp wykrywa to już na etapie handshake TLS, zanim w ogóle odczyta nagłówek User-Agent. Testowałem to wielokrotnie — świeże IP, realistyczne nagłówki, losowe opóźnienia — i mimo to natychmiast dostawałem 403 Forbidden przy zwykłym requests.
Macierz wyboru biblioteki
| Biblioteka | Najlepsza do | Obsługuje JS? | Antybot? | Krzywa nauki | Szybkość |
|---|---|---|---|---|---|
requests + BeautifulSoup | ❌ | ❌ | Bardzo niska | Szybka (dopóki nie zostanie zablokowana) | |
httpx async + parsel | Duże, asynchroniczne scrapowanie | ❌ | ❌ | Niska | Bardzo szybka |
curl_cffi + parsel | Specyficznie Yelp: imitacja TLS | ❌ | ✅ TLS/JA3/HTTP2 | Niska | Bardzo szybka |
Scrapy 2.14 | Pełne pipeline’y z paginacją | Częściowo (przez scrapy-playwright) | AutoThrottle, retry middleware | Średnia-wysoka | Szybka |
Selenium 4.43 / Playwright 1.58 | Strony mocno oparte na JS, obejścia CAPTCHA | ✅ | Częściowo | Średnia | Wolna (~10–30 stron/min) |
| Thunderbit | Osoby nietechniczne, szybka ekstrakcja | ✅ (przeglądarka) | Wbudowane (tryb Cloud) | Bardzo niska | Szybka |
Odkrycie z curl_cffi
Biblioteką, która zmieniła mój workflow scrapowania Yelp, jest curl_cffi — Pythonowy binding do curl-impersonate. Generuje dokładnie taki sam fingerprint TLS/JA3 + HTTP/2 jak prawdziwy Chrome, a jego API jest praktycznie zamiennikiem requests:
from curl_cffi import requests
r = requests.get(
"https://www.yelp.com/biz/some-restaurant",
impersonate="chrome131",
)
print(r.status_code, len(r.text))
Ta jedna zmiana — from curl_cffi import requests oraz impersonate="chrome131" — omija największą warstwę antybota Yelp bez uruchamiania przeglądarki. W moich testach to różnica między natychmiastowym 403 a czystą odpowiedzią 200.
Polecany stack do Yelp w 2025 roku: curl_cffi + parsel + jmespath + residential proxies. Jeśli potrzebujesz pełnego pipeline’u z harmonogramem, owiń to w Scrapy 2.14 i użyj middleware downloadera opartego na curl_cffi.
Konfiguracja środowiska Pythona do scrapowania Yelp
- Poziom trudności: średni
- Czas potrzebny: około 15 minut na konfigurację, 1–2 godziny na działającego scrapera
- Czego potrzebujesz: Python 3.10+ (zalecany 3.12), terminal i opcjonalnie dostawca residential proxy
Krok 1: Utwórz środowisko wirtualne i zainstaluj pakiety
python3.12 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install "curl_cffi>=0.11" "parsel>=1.9" "jmespath>=1.0" pandas
Co robi każdy pakiet:
curl_cffi— wysyła żądania HTTP z fingerprintem TLS Chrome’a (obejście antybota)parsel— selektory CSS/XPath do parsowania HTML (ten sam silnik co Scrapy, ale lżejszy)jmespath— deklaratywne zapytania do JSON (czytelniejsze niż zagnieżdżone odwołania do słowników w JSON-ie Yelp)pandas— eksport danych do CSV/Excela
Opcjonalnie, ale przydatne:
pip install fake-useragent # Uwaga: repo zarchiwizowane w kwietniu 2026, ale nadal możliwe do instalacji
Krok po kroku: jak scrapować Yelp w Pythonie
To jest główny tutorial. Kluczowa zasada, która sprawia, że wszystko jest znacznie bardziej odporne na zmiany: omijaj selektory CSS i pobieraj ukryty JSON. Yelp losowo zmienia nazwy klas CSS przy buildzie (y-css-14xwok2 w jednym tygodniu, y-css-hcq7b9 w następnym), więc każdy scraper oparty na tych klasach psuje się w ciągu kilku tygodni. Osadzone payloady JSON — application/ld+json i react-root-props — są stabilne.
Krok 2: Scrapowanie wyników wyszukiwania Yelp
Adresy wyszukiwania Yelp mają przewidywalny format: https://www.yelp.com/search?find_desc={term}&find_loc={location}. Dane z wyników są osadzone w tagu <script data-id="react-root-props"> jako JSON — a nie w bałaganie klas CSS.
import re, json, jmespath
from curl_cffi import requests
from parsel import Selector
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": "text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8",
"accept-language": "en-US,en;q=0.9",
"accept-encoding": "gzip, deflate, br",
"cookie": "intl_splash=false",
}
def scrape_search(term: str, location: str, max_pages: int = 3):
results = []
for page in range(max_pages):
url = (f"https://www.yelp.com/search?"
f"find_desc={term}&find_loc={location}&start={page * 10}")
r = requests.get(url, headers=HEADERS, impersonate="chrome131")
if r.status_code != 200:
print(f"Blocked on page {page}: {r.status_code}")
break
sel = Selector(text=r.text)
script = sel.xpath(
"//script[@data-id='react-root-props']/text()"
).get() or ""
m = re.search(r"react_root_props\s*=\s*(\{.*?\});", script, re.S)
if not m:
print(f"No react-root-props found on page {page} — possible soft block")
break
data = json.loads(m.group(1))
businesses = jmespath.search(
"legacyProps.searchAppProps.searchPageProps"
".mainContentComponentsListProps"
"[?searchResultBusiness].searchResultBusiness.{"
"name: name, url: businessUrl, rating: rating, "
"reviews: reviewCount, phone: phone, "
"neighborhoods: neighborhoods}",
data,
) or []
results.extend(businesses)
import time, random
time.sleep(random.uniform(3, 7))
return results
Powinieneś dostać listę słowników z nazwami firm, URL-ami, ocenami i liczbą recenzji. Jeśli react-root-props nie ma w odpowiedzi, dostałeś „shell” blokady — zmień IP i spróbuj ponownie.
Nagłówek Cookie: intl_splash=false to standardowy workaround dla krajowego ekranu powitalnego Yelp. Bez niego IP spoza USA trafiają na stronę splash, która wygląda jak miękka blokada, ale nią nie jest.
Krok 3: Scrapowanie stron firm w Yelp
Każdy URL firmy z wyników wyszukiwania prowadzi do strony szczegółów z bogatszym zestawem danych. Najstabilniejszym celem ekstrakcji jest blok <script type="application/ld+json"> — zawiera uporządkowane dane schema.org, które Yelp utrzymuje dla SEO i nie zaciemnia.
def scrape_business(biz_url: str) -> dict:
url = f"https://www.yelp.com{biz_url}" if biz_url.startswith("/") else biz_url
r = requests.get(url, headers=HEADERS, impersonate="chrome131")
if r.status_code != 200:
return {"url": url, "error": r.status_code}
sel = Selector(text=r.text)
biz_id = sel.css('meta[name="yelp-biz-id"]::attr(content)').get()
for raw in sel.css('script[type="application/ld+json"]::text').getall():
try:
data = json.loads(raw)
except json.JSONDecodeError:
continue
for node in (data if isinstance(data, list) else [data]):
if node.get("@type") in (
"Restaurant", "LocalBusiness", "FoodEstablishment",
"HealthAndBeautyBusiness", "HomeAndConstructionBusiness",
):
return {
"biz_id": biz_id,
"name": node.get("name"),
"rating": (node.get("aggregateRating") or {}).get("ratingValue"),
"review_count": (node.get("aggregateRating") or {}).get("reviewCount"),
"address": node.get("address"),
"telephone": node.get("telephone"),
"price_range": node.get("priceRange"),
"hours": node.get("openingHours"),
"url": url,
}
return {"biz_id": biz_id, "url": url}
Wartość meta[name="yelp-biz-id"] to zakodowane ID firmy, którego będziesz potrzebować do endpointu z recenzjami. Pobierz je tutaj — użyjesz go w następnym kroku.
Krok 4: Scrapowanie recenzji Yelp z paginacją
To tutaj Fusion API przegrywa, a scrapowanie wygrywa. Wewnętrzny endpoint GraphQL Yelp zwraca pełny tekst recenzji, informacje o recenzencie, daty, oceny i liczbę głosów — wszystko to, co API ukrywa.
Endpoint to https://www.yelp.com/gql/batch, a dla operacji GetBusinessReviewFeed używa statycznego documentId. Paginacja działa przez cursor zakodowany w base64.
import base64
GQL_URL = "https://www.yelp.com/gql/batch"
DOC_ID = "ef51f33d1b0eccc958dddbf6cde15739c48b34637a00ebe316441031d4bf7681"
def fetch_reviews(enc_biz_id: str, num_pages: int = 5):
all_reviews = []
for page in range(num_pages):
offset = page * 10
cursor = base64.b64encode(
json.dumps({"version": 1, "offset": offset}).encode()
).decode()
payload = [{
"operationName": "GetBusinessReviewFeed",
"variables": {
"encBizId": enc_biz_id,
"reviewsPerPage": 10,
"after": cursor,
"sortBy": "DATE_DESC",
"language": "en",
},
"extensions": {
"operationType": "query",
"documentId": DOC_ID,
},
}]
r = requests.post(
GQL_URL,
json=payload,
headers={
**HEADERS,
"content-type": "application/json",
"x-apollo-operation-name": "GetBusinessReviewFeed",
"apollographql-client-name": "yelp-main-frontend",
},
impersonate="chrome131",
)
if r.status_code != 200:
print(f"Review fetch failed at offset {offset}: {r.status_code}")
break
data = r.json()
# Navigate the response structure to extract reviews
try:
reviews = data[0]["data"]["business"]["reviews"]["edges"]
for edge in reviews:
node = edge.get("node", {})
all_reviews.append({
"reviewer": node.get("author", {}).get("displayName"),
"rating": node.get("rating"),
"date": node.get("localizedDate"),
"text": node.get("text", {}).get("full"),
})
except (KeyError, IndexError, TypeError):
break
import time, random
time.sleep(random.uniform(3, 7))
return all_reviews
Każda strona zwraca 10 recenzji. Zwiększaj offset w cursorze base64, aby przechodzić dalej. Parametr sortBy przyjmuje DATE_DESC (najnowsze najpierw), RATING_ASC, RATING_DESC i inne.
Krok 5: Eksport danych z Yelp
import pandas as pd
# Zakładając, że zebrałeś firmy i recenzje
df_businesses = pd.DataFrame(businesses)
df_businesses.to_csv("yelp_businesses.csv", index=False)
df_reviews = pd.DataFrame(all_reviews)
df_reviews.to_csv("yelp_reviews.csv", index=False)
# Albo zapisz jako JSON dla większej elastyczności
import json
with open("yelp_data.json", "w") as f:
json.dump({"businesses": businesses, "reviews": all_reviews}, f, indent=2)
Dla osób wybierających no-code Thunderbit eksportuje te same dane bezpośrednio do Excel, Google Sheets, Airtable lub Notion — bez pandas i bez kodu do zapisu plików.
Playbook antyblokowy: jak scrapować Yelp bez blokad
To jest najważniejsza część artykułu. Zabezpieczenia antybotowe Yelp stały się znacznie ostrzejsze od końca 2024 roku — TLS fingerprinting, sprawdzanie reputacji IP, CAPTCHA i analiza zachowania są dziś standardem. Większość istniejących poradników jest już nieaktualna, bo powstała przed tą ofensywą.

Strategia musi być wielowarstwowa. Każda warstwa zmniejsza współczynnik blokad; razem pozwalają na długotrwałe scrapowanie.
Warstwa 1: realistyczne nagłówki żądań
Domyślne nagłówki requests w Pythonie wysyłają User-Agent: python-requests/2.x — blokada następuje natychmiast. Ale sam realistyczny User-Agent nie wystarczy. Yelp sprawdza cały zestaw nagłówków Client Hints pod kątem spójności.
FULL_HEADERS = {
"authority": "www.yelp.com",
"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": "text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8",
"accept-language": "en-US,en;q=0.9",
"accept-encoding": "gzip, deflate, br",
"sec-ch-ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"referer": "https://www.yelp.com/",
"cookie": "intl_splash=false",
}
Trzy błędy, które mogą Cię oznaczyć jako bota:
- UA twierdzi, że to Chrome, ale
sec-ch-uanie istnieje albo kłóci się z wersją UA sec-ch-ua-platformmówi „Windows”, a string UA wskazuje macOS- Identyczny UA przy tysiącach żądań z jednego IP — rotuj pulę 10–20 aktualnych stringów Chrome/Firefox/Safari
Warstwa 2: limitowanie tempa i losowe opóźnienia
Przewidywalne wzorce czasowe są sygnałem ostrzegawczym. Dodaj losowe pauzy i wdroż exponential backoff przy błędach.
import random, time
def polite_get(client_get, url, attempt=0):
r = client_get(url, headers=FULL_HEADERS, impersonate="chrome131")
if r.status_code in (403, 429, 503):
if attempt >= 4:
raise RuntimeError(f"Blocked after {attempt + 1} attempts on {url}")
backoff = 2 ** (attempt + 1) + random.random()
print(f" Got {r.status_code}, backing off {backoff:.1f}s (attempt {attempt + 1})")
time.sleep(backoff)
return polite_get(client_get, url, attempt + 1)
time.sleep(random.uniform(3, 7))
return r
| Parametr | Zalecana wartość |
|---|---|
| Losowa przerwa między żądaniami | random.uniform(3, 7) sekund |
| Backoff przy 429/403/503 | 2 → 4 → 8 → 16 s, maks. 5 prób |
| Jednoczesne workery na jedno IP | 1 (serializuj per IP; do równoległości używaj proxy) |
| Maks. stabilny poziom dla residential IP | ok. 1 żądanie / 5 s (~12 rpm) |
Warstwa 3: rotacja User-Agentów i sesji
Rotuj pulę prawdziwych stringów User-Agent przeglądarek. Zachowuj sesje i ciasteczka, aby naśladować naturalne przeglądanie — Yelp używa detekcji opartej na cookies, więc tworzenie nowej sesji dla każdego żądania też wygląda podejrzanie.
UA_POOL = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14.4; rv:125.0) Gecko/20100101 Firefox/125.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/605.1.15 Safari/17.4.1",
# Dodaj 5–10 kolejnych aktualnych stringów
]
Warstwa 4: rotacja proxy
Przy realnej skali potrzebujesz residential proxy. Datacenter i darmowe proxy nie działają na Yelp — warstwa reputacji IP prewencyjnie blokuje zakresy AWS, GCP i DigitalOcean.
| Dostawca | Cena startowa $/GB | Uwagi |
|---|---|---|
| IPRoyal | $1.75/GB | Najtańszy; prowadzi najczęściej cytowany poradnik o Yelp |
| Decodo (dawniej Smartproxy) | $3.20–$3.50 | Najlepszy stosunek GB/cena przy większej skali |
| Bright Data | $4.00 (PAYG) | Pula 150M+ IP; osobna strona Yelp Proxies |
| Oxylabs | $6.00–$8.00 | Premium; 10M+ IPs |
| Aluvia (mobile SIM) | $3.00 | Prawdziwe mobilne IP amerykańskich operatorów, ukierunkowane na Yelp |
Rotujące residential IP (nowy adres przy każdym żądaniu) sprawdzają się najlepiej przy wysokiej liczbie zapytań do wyszukiwania. Sticky sessions (jedno IP przez 10 minut) są lepsze, gdy chcesz utrzymać cookies w przepływie strona firmy → recenzje → paginacja.
Warstwa 5: wykrywanie blokad i reagowanie na nie
Nie każda blokada wygląda tak samo. Yelp często serwuje ogólny shell „page not available” zamiast CAPTCHA, przez co naiwne scrapery myślą, że dostały dane, podczas gdy w rzeczywistości otrzymały pustą odpowiedź.
BLOCK_MARKERS = (
"captcha", "px-captcha", "page not available",
"access denied", "unusual traffic",
)
def is_blocked(resp):
if resp.status_code in (401, 403, 429, 503):
return True
body = resp.text.lower()
if any(m in body for m in BLOCK_MARKERS):
return True
# Jeśli to strona wyszukiwania/firmy, ale nie ma react-root-props,
# Yelp wysłał okrojoną odpowiedź blokującą
if "react-root-props" not in body and "/biz/" in str(resp.url):
return True
return False
| Sygnał | Znaczenie |
|---|---|
| HTTP 403 | Twarda blokada — IP/nagłówki/TLS spalony |
| HTTP 429 | Limit tempa — często do odzyskania dzięki backoff |
| HTTP 503 | Ogólna blokada albo przeciążenie |
Przekierowanie do /error lub treść „page not available” | Miękka blokada |
| Pusty z samymi | Strona challenge, czeka na JS |
captcha / g-recaptcha / px-captcha w treści | Eskalacja — wymagana CAPTCHA |
Brak react-root-props na stronie listingu | Okrojona odpowiedź blokująca |
Warstwa 6: odporny trik parsowania — ukryty JSON zamiast selektorów CSS
Warto powtórzyć: Yelp losuje nazwy klas CSS przy buildzie. Scraper przypięty do h3.y-css-14xwok2 przestanie działać w ciągu kilku tygodni, gdy Yelp wdroży h3.y-css-hcq7b9.
Payloady, które nie zmieniają się tak łatwo:
<script type="application/ld+json">— dane schema.org (nazwa, adres, telefon, ocena, godziny)<script data-id="react-root-props">— pełne dane wyników wyszukiwania jako JSONhttps://www.yelp.com/gql/batch— endpoint GraphQL z recenzjami i stabilnymdocumentId
Jeśli parsujesz klasy CSS, budujesz na piasku. Parsuj JSON.
Warstwa 7: awaryjny tryb stealth browser
Przechodź do headless browsera tylko wtedy, gdy curl_cffi + residential proxies nie wystarczają — zwykle gdy Yelp pokazuje stronę challenge JS albo CAPTCHA.
Dla 95% scrapowania firm, wyników i recenzji curl_cffi + ukryty JSON + residential proxies jest szybsze, tańsze i bardziej niezawodne niż przeglądarka. Ale jeśli już potrzebujesz browsera:
| Narzędzie | Status (2025) | Uwagi |
|---|---|---|
| rebrowser-playwright | Polecany punkt startowy | Zmieniony Playwright z poprawkami pod wycieki CDP |
| nodriver | Najlepszy stealth dla Chrome | Następca undetected-chromedriver; omija protokół WebDriver |
| patchright | Aktywnie rozwijany fork Playwright | Przechodzi nowoczesne testy detekcji |
| playwright-stealth | Dojrzały | Łata navigator.webdriver, usuwa HeadlessChrome z UA |
Na Yelp pomiń zwykłe Selenium. Jest zbyt łatwe do fingerprintingu.
Yelp Fusion API vs scrapowanie w Pythonie vs Thunderbit: pełne porównanie
| Wymiar | Yelp Fusion API | Scrapowanie w Pythonie | Thunderbit |
|---|---|---|---|
| Pełny tekst recenzji | ❌ 3 fragmenty × ~160 znaków | ✅ Bez limitu (GraphQL) | ✅ Wbudowany szablon recenzji |
| Metadane recenzji (głosy, odpowiedzi właściciela) | ❌ | ✅ | ✅ przez pola sugerowane przez AI |
| Zdjęcia | ❌ (0 w planie Base) | ✅ Bez limitu | ✅ |
| Maks. wyników na wyszukiwanie | 240 (wcześniej 1 000 przed 2024) | Bez limitu (z paginacją) | Bez limitu |
| Dzienny limit | 300–500 (nowi) / 5 000 (legacy) | Tylko budżet na proxy | Oparte na kredytach (3 000/mies. w Pro) |
| Czas konfiguracji | ~15 min | Od kilku godzin do kilku dni | ~2 minuty |
| Obsługa antybota | N/D | Twój problem | Obsługiwane (tryb Cloud) |
| Ryzyko prawne | Niskie (oficjalne) | Średnie (szara strefa ToS) | Średnie (tak samo jak scrapowanie) |
| Koszt startowy | $29/mies. | ok. $0.75–$4/GB proxy + czas dev | Darmowy plan |
| Koszt przy dużym użyciu | $643+/mies. | $50–$500/mies. proxy + czas dev | $38–$49/mies. |
| Eksport danych | JSON | CSV/JSON (musisz napisać) | Excel / Sheets / Airtable / Notion — za darmo |
| Utrzymanie | Niskie | Wysokie (selektory się psują, antybot się zaostrza) | Niskie (AI dostosowuje się ponownie) |
Wskazówki prawne i etyczne dotyczące scrapowania Yelp
Nie jestem prawnikiem i to nie jest porada prawna. Ale krajobraz prawny zmienił się na tyle w ciągu ostatnich dwóch lat, że warto znać podstawy, zanim zainwestujesz czas w projekt scrapowania Yelp.
Co mówi regulamin Yelp: Aktualizacja ToS z października 2025 wprost zabrania używania „jakiegokolwiek robota, pająka... lub innego zautomatyzowanego narzędzia” do „uzyskiwania dostępu, pobierania, kopiowania, scrapowania lub indeksowania jakiejkolwiek części Usługi”. Dodano też zapis o „AI Technologies i/lub innych zautomatyzowanych narzędziach”.
Wsparcie Yelp potwierdza: „Yelp nie zezwala na żadne scrapowanie strony.”
Co mówi robots.txt: Plik robots.txt ma wildcard User-agent: * / Disallow: / i dodatkowo blokuje GPTBot, ClaudeBot, PerplexityBot, CCBot oraz Meta-ExternalAgent. Białą listę mają tylko Googlebot, Bingbot i kilka crawlerów społecznościowych.
Istotny precedens prawny: W sprawie Meta v. Bright Data (N.D. Cal. styczeń 2024) sąd orzekł, że scrapowanie publicznie dostępnych danych bez logowania nie naruszało ToS Meta. Kluczowe rozróżnienie to: publiczne dane bez logowania vs. dane za logowaniem. Sprawa hiQ v. LinkedIn pokazała, że scrapowanie publicznych danych prawdopodobnie nie narusza CFAA, ale hiQ przegrało nadal na roszczeniach stanowych (trespass to chattels, misappropriation) i dostało wyrok na $500 000.
Praktyczne zasady:
- Scrapuj wyłącznie publicznie dostępne strony bez logowania
- Ograniczaj tempo żądań (opóźnienia w tym poradniku pełnią też funkcję etycznego rate limitu)
- Nie odsprzedawaj surowego tekstu recenzji przypisanego do konkretnych użytkowników — szanuj prywatność recenzentów
- Przestrzegaj lokalnych przepisów o ochronie danych (CCPA, GDPR)
- Nie loguj się, żeby scrapować — to przekracza granicę autoryzacji
- Traktuj informacje o firmie (nazwa/adres/telefon/ocena) jako publiczne dane faktyczne; tekst recenzji traktuj jako bardziej wrażliwy
W swojej konkretnej sytuacji skonsultuj się z prawnikiem.
Podsumowanie
Trzy ścieżki, jeden cel.
Yelp Fusion API to opcja oficjalna i mało wymagająca w utrzymaniu — ale ogranicza się do 3 fragmentów recenzji i zaczyna się od $29/miesiąc. Scrapowanie w Pythonie daje pełną kontrolę nad każdym punktem danych w Yelp, ale wymaga realnej inwestycji: curl_cffi do imitacji TLS, residential proxies, losowych opóźnień, parsowania ukrytego JSON-a i stałego utrzymania wraz z rozwojem zabezpieczeń Yelp. Thunderbit pozwala przejść od „potrzebuję danych z Yelp” do „oto mój arkusz” w około 30 sekund, bez kodu i bez konfiguracji proxy.
Elementy antyblokowe, które naprawdę działają w 2025 roku: realistyczne nagłówki z pełnym Client Hints, curl_cffi do imitacji fingerprintu TLS, losowe opóźnienia z exponential backoff, rotacja residential proxy oraz — co najważniejsze — parsowanie ukrytego JSON-a (application/ld+json i react-root-props) zamiast kruchych selektorów CSS.
Nie wiesz, która ścieżka będzie najlepsza? Najpierw wypróbuj darmowy plan Thunderbit. Jeśli wystarczy, oszczędzasz sobie godzin pracy. Jeśli potrzebujesz większej kontroli — pełnych pipeline’ów programistycznych, własnych pól, ścisłej integracji z CRM — powyższy przewodnik po Pythonie Cię prowadzi. A jeśli chcesz szerzej spojrzeć na krajobraz narzędzi do scrapowania, sprawdź nasze zestawienie najlepszych rozszerzeń Chrome do web scrapingu albo przewodnik jak pobrać dane ze strony do Excela.
Wypróbuj Thunderbit do ekstrakcji danych z Yelp Get Started Free
FAQ
Czy mogę scrapować Yelp za darmo w Pythonie?
Tak — używając darmowych bibliotek, takich jak curl_cffi, parsel i jmespath. Ale przy jakiejkolwiek realnej skali (więcej niż kilka dziesiątek stron) będziesz potrzebować płatnych residential proxy, które zaczynają się od około $1.75/GB w IPRoyal. Thunderbit oferuje też darmowy plan z 6 stronami miesięcznie dla szybkiej ekstrakcji no-code.
Czy Yelp blokuje scrapery?
Tak, i to agresywnie. Yelp stosuje TLS/JA3 fingerprinting, scoring reputacji IP, CAPTCHA, analizę zachowania oraz rotujące, zaciemnione klasy CSS. Zwykłe requests jest blokowane przy pierwszym żądaniu. Warstwowa strategia antyblokowa z tego przewodnika — curl_cffi do imitacji TLS, realistyczne nagłówki, losowe opóźnienia i residential proxies — to rozwiązanie działające w 2025 roku.
Czy Yelp Fusion API jest lepsze niż scrapowanie?
To zależy od potrzeb. API jest oficjalne i niskiego ryzyka, ale zwraca tylko 3 fragmenty recenzji po ok. 160 znaków każdy, ogranicza wyniki wyszukiwania do 240 i startuje od $29/miesiąc. Jeśli potrzebujesz pełnego tekstu recenzji, metadanych recenzji albo więcej niż kilkuset rekordów dziennie, scrapowanie to jedyna opcja.
Jak scrapować recenzje Yelp w Pythonie?
Użyj curl_cffi z impersonate="chrome131", aby pobrać stronę firmy, wyciągnij zakodowane ID firmy z <meta name="yelp-biz-id">, a następnie wyślij POST do https://www.yelp.com/gql/batch z operacją GetBusinessReviewFeed i przechodź przez wyniki za pomocą base64-owanego kursora after. Kod krok po kroku znajdziesz w sekcji tutoriala powyżej. Repozytorium Scrapfly Yelp scraper repo to również solidny punkt odniesienia.
Czy mogę scrapować Yelp bez kodowania?
Tak — AI Web Scraper Thunderbit ma gotowe szablony Yelp business i reviews. Otwórz stronę Yelp, kliknij AI Suggest Fields, kliknij Scrape. Eksport do Google Sheets, Excel, Airtable i Notion jest darmowy na każdym planie, także bezpłatnym.
Dowiedz się więcej
- Jak robić web scraping bez blokad w Pythonie
- Python Web Scraping: jak unikać blokad dzięki inteligentnemu użyciu proxy
- Opanuj web scraper w Pythonie: przewodnik krok po kroku
- Poradnik web scrapingu w Pythonie: pobieranie danych ze strony internetowej
- Poradnik web scrapingu w Pythonie: jak scrapować stronę internetową


