Как я парсю Yelp на Python и не ловлю блокировки

Последнее обновление: April 15, 2026
Как я парсю Yelp на Python и не ловлю блокировки

Yelp хранит 330 миллионов накопленных отзывов на 8,4 миллиона активных страниц компаний — и превратить эти данные в удобный формат стало как никогда сложно. Антибот-кампания Yelp в 2024–2025 годах тихо поломала большую часть существующих Python-руководств по парсингу.

Если ты недавно пытался запустить Yelp scraper и упирался в стену из ошибок 403, пустых HTML-ответов или CAPTCHA, которых ещё полгода назад не было, тебе это не кажется. Сейчас Yelp использует TLS/JA3-фингерпринтинг, постоянно меняющиеся обфусцированные CSS-классы и жёсткую оценку репутации IP-адресов — из-за этого старый подход requests + BeautifulSoup, который до сих пор советуют почти все туториалы, ломается с первого же запроса. Я несколько недель тестировал разные способы работы с текущей защитой Yelp и в этом гайде собрал всё, что реально работает в 2025 году: официальный Fusion API (и почему его, скорее всего, будет недостаточно), полноценный Python-воркфлоу со слоистой стратегией обхода блокировок и no-code альтернативу в 2 клика с Thunderbit для тех, кому нужны просто данные, а не марафон по отладке.

Попробовать Thunderbit для парсинга Yelp

Зачем парсить Yelp на Python и кому это вообще нужно

Прежде чем писать хоть строчку кода, стоит понять: какую реальную бизнес-задачу решают данные Yelp? Это не просто сайт с отзывами о ресторанах — по сути, это живая база локальных компаний со структурированными контактами, рейтингами, категориями, графиком работы и сотнями миллионов пользовательских отзывов.

yelp_stats_bd6a43108e.png

Вот кому такие данные полезнее всего и что именно они обычно вытаскивают:

СценарийКлючевые поляПочему это важно
Продажи и лидогенерацияНазвание компании, телефон, сайт, адрес, категория, рейтингСобирайте целевые списки локальных SMB — 4 из 5 пользователей Yelp уже готовы к покупке прямо на входе
Конкурентная разведкаОтзывы, звёздный рейтинг, количество отзывов, тональностьСледите за репутацией конкурентов, находите слабые места в сервисе, отслеживайте тренды
Маркетинговые исследования и NLPПолный текст отзывов, даты, метаданные автораАнализ тональности, тематическое моделирование — отзывы Yelp один из самых используемых NLP-корпусов в академических исследованиях
Недвижимость и выбор локацииПлотность бизнеса, сочетание категорий, качество отзывов по районамПодбор точек для франшиз и ритейла — Yelp продаёт Location Intelligence как лицензированный B2B-продукт именно для этого
Ecommerce и операционные процессыСигналы о ценах, жалобы клиентов, часы работыОтслеживайте, как оценивают конкурентов, и выявляйте операционные паттерны

Смысл в целом один: конечная цель — структурированные данные, а Python — лишь один из способов их получить. Кому-то нужен полный программный контроль. А кому-то достаточно таблицы с контактами сантехников в Остине. В этом материале разобраны оба подхода.

Yelp Fusion API vs. Python web scraping: что выбрать?

Большинство гайдов вообще пропускают этот шаг и сразу переходят к коду, не проверяя, хватило бы официального Yelp Fusion API (сейчас оно переименовано в "Yelp Places API"). На практике такой анализ экономит часы впустую потраченного времени — потому что API отлично подходит для одних задач и совершенно не годится для других.

Что реально даёт Fusion API

Fusion API предоставляет структурированный поиск компаний, подробности о бизнесе, автодополнение и endpoint с отзывами. Это официальный, документированный способ доступа, без танцев с антибот-защитой.

Но всё ломается именно на endpoint с отзывами. Вот что подтвердили сотрудники Yelp на GitHub:

"Yelp API не возвращает полный текст отзыва. По умолчанию выдаются три фрагмента по 160 символов." — ответ сотрудника Yelp, GitHub Issue #163

Это не баг, а задумка. API физически ограничивает выдачу 3 фрагментами отзывов (7 на Premium), каждый из них обрезан примерно до 160 символов. Нет метаданных отзывов (useful/funny/cool), нет истории автора, нет ответов владельца. А дневной лимит для новых клиентов после мая 2023 года снизился до 300–500 запросов в день — раньше было 5 000. Стартовая цена начинается от $29 в месяц.

Как принять решение

ФакторYelp Fusion APIPython scrapingThunderbit (no-code)
Полные отзывы❌ Только 3 фрагмента (~160 символов каждый)✅ Все отзывы через GraphQL✅ Все видимые отзывы
Лимиты запросов300–500/день (новые); 5 000 (старые)Вы управляете сами (и бюджетом на прокси)По кредитной модели
Сложность запуска~15 минут (API-ключ + SDK)От часов до дней~2 минуты
Поля о бизнесе~20 структурированных полейБез ограничений (HTML/JSON-парсинг)Поля, предлагаемые ИИ
Обход антиботаНе нуженНужно реализовать самомуОбрабатывается автоматически
Юридический риск✅ Официально разрешено⚠️ Серая зона ToS⚠️ Тот же риск, что и у парсинга
Стоимостьминимум $29/месБесплатно (+ прокси $0.75–$4/GB)Есть бесплатный тариф
ПоддержкаНизкая (API стабилен)Высокая (селекторы ломаются, антибот усиливается)Низкая (ИИ подстраивается)

Используй Fusion API, если: тебе нужны базовые данные о бизнесе, небольшие выборки или официальный интеграционный сценарий — и достаточно 3 фрагментов отзывов на компанию.

Используй Python scraping, если: нужны полные тексты отзывов, все отзывы по компании, метаданные отзывов, больше 240 результатов в поиске или бюджет ниже $29 в месяц.

Используй Thunderbit, если: хочешь быстро получить данные без написания и поддержки кода. Ниже я подробнее покажу no-code вариант.

No-code shortcut: парсинг Yelp через Thunderbit без Python

Прежде чем переходить к Python, вот самый быстрый путь для тех, кому нужны данные, а не упражнения в программировании. Почти все конкурирующие гайды подразумевают, что ты знаешь Python, но в работе с Thunderbit я вижу, что огромная часть людей, которые ищут "scrape Yelp", — это sales-менеджеры, операционные специалисты и владельцы малого бизнеса. Им нужна таблица с локальными компаниями, а не ускоренный курс по TLS-фингерпринтингу.

Thunderbit уже предлагает готовые шаблоны для Yelp:

  • Yelp Business Web Scraper — извлекает название компании, рейтинг, контакты, адрес, часы работы, категорию
  • Yelp Review Scraper — извлекает имя пользователя, текст отзыва, рейтинг, дату, локацию автора

Как это работает на практике

  1. Открой страницу результатов поиска Yelp или страницу компании в Chrome
  2. Нажми AI Suggest Fields в расширении Thunderbit — ИИ прочитает страницу и предложит столбцы (название компании, рейтинг, число отзывов, ценовой диапазон, категория, адрес, телефон, URL)
  3. Нажми Scrape — готово

Если ты используешь готовые шаблоны Yelp, всё ещё проще: открой шаблон и нажми Scrape.

Парсинг вложенных страниц автоматически обрабатывает enrichment-цикл — начни со страницы поиска Yelp, включи subpage scraping, и Thunderbit сам зайдёт на страницу каждой компании, чтобы собрать часы работы, полные отзывы, сайт, фото и удобства. Без дополнительной настройки.

Пагинация автоматическая — и для кликов, и для бесконечной прокрутки, всё работает из коробки. (Подробнее о том, как это устроено, см. наш гид по пагинации.)

Экспорт бесплатен на всех тарифах — Excel, Google Sheets, Airtable, Notion, CSV, JSON. Никакого pandas и никакого кода для записи файлов.

Сравнение по времени

ВремяPython scraperThunderbit
Первый запускЧасы или дни (селекторы, пагинация, прокси, retry-логика)~30 секунд с готовым шаблоном Yelp
Когда Yelp меняет разметкуВручную переписывать селекторыСнова нажать AI Suggest Fields — всё подстроится автоматически
Когда блокируют IPОтладка, ротация пулов прокси, повторные тестыCloud mode сам ротирует IP
Экспорт в Google SheetsПисать OAuth и связку с pandasОдин клик, бесплатно

Если после первой попытки Thunderbit закрывает твою задачу, можно смело пропустить оставшуюся часть статьи. Если же тебе нужен полный программный контроль, кастомные поля или масштаб свыше нескольких тысяч записей в месяц — читай дальше.

Python-библиотеки для парсинга Yelp: что выбрать

"Что лучше использовать для Yelp — Scrapy, BS4+requests или Selenium?" — один из самых частых вопросов в обсуждениях r/webscraping. И при этом в каждом туториале просто выбирают любимую библиотеку и идут дальше, не объясняя почему. Вот честный разбор.

Реальность 2025 года: requests + BeautifulSoup для Yelp сломан

Стек, который до сих пор советуют в каждом классическом гайде по Yelp — pip install requests beautifulsoup4получает блокировку уже на первом запросе в 2025 году. Не на 50-м. На первом.

Причина в том, что библиотека requests отправляет TLS/JA3-фингерпринт, который не совпадает ни с одним реальным браузером. Антибот-слой Yelp вычисляет это ещё на уровне TLS-handshake, до того как вообще прочитает заголовок User-Agent. Я проверял это много раз — свежий IP, реалистичные заголовки, случайные задержки — и всё равно моментально получал 403 Forbidden на обычном requests.

Матрица выбора библиотеки

БиблиотекаЛучше всего подходит дляРаботает с JS?Обход антибота?Порог входаСкорость
requests + BeautifulSoupПростой парсинг одной страницы (для Yelp сломано)Очень низкийБыстро, пока не заблокировали
httpx async + parselАсинхронный парсинг в большом объёмеНизкийОчень быстро
curl_cffi + parselСпециально для Yelp: имитация TLS✅ TLS/JA3/HTTP2НизкийОчень быстро
Scrapy 2.14Полноценные crawl-пайплайны с пагинациейЧастично (через scrapy-playwright)AutoThrottle, retry middlewareСредне-высокийБыстро
Selenium 4.43 / Playwright 1.58Страницы с тяжёлым JS, обход CAPTCHAЧастичноСреднийМедленно (~10–30 страниц/мин)
ThunderbitНепрограммисты, быстрое извлечение✅ (в браузере)Встроено (Cloud mode)Очень низкийБыстро

Почему curl_cffi — это находка

Библиотека, которая изменила мой рабочий процесс для Yelp, — это curl_cffi, Python-обёртка над curl-impersonate. Она выдаёт точно такой же TLS/JA3 + HTTP/2-фингерпринт, как настоящий Chrome, а её API почти полностью заменяет 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))

Одно это изменение — from curl_cffi import requests плюс impersonate="chrome131" — позволяет обойти крупнейший слой антибот-защиты Yelp без запуска браузера. По моим тестам, это разница между мгновенным 403 и чистым ответом 200.

Мой рекомендуемый стек для Yelp в 2025 году: curl_cffi + parsel + jmespath + residential proxies. Если нужен полный пайплайн с расписанием, можно обернуть это в Scrapy 2.14 с downloader middleware на базе curl_cffi.

Настраиваем Python-окружение для парсинга Yelp

  • Сложность: средняя
  • Время: около 15 минут на настройку, 1–2 часа на рабочий скрапер
  • Что понадобится: Python 3.10+ (рекомендуется 3.12), терминал и, по желанию, провайдер residential proxy

Шаг 1: Создайте виртуальное окружение и установите пакеты

python3.12 -m venv .venv
source .venv/bin/activate  # На Windows: .venv\Scripts\activate
pip install "curl_cffi>=0.11" "parsel>=1.9" "jmespath>=1.0" pandas

Что делает каждый пакет:

  • curl_cffi — отправляет HTTP-запросы с TLS-фингерпринтом Chrome (обход антибота)
  • parsel — CSS/XPath-селекторы для парсинга HTML (тот же движок, что у Scrapy, но легче)
  • jmespath — декларативные запросы к JSON (удобнее, чем вручную лазить по вложенным словарям Yelp)
  • pandas — экспорт данных в CSV/Excel

Дополнительно, но полезно:

pip install fake-useragent  # Примечание: репозиторий архивирован в апреле 2026, но установка всё ещё работает

Пошагово: как парсить Yelp на Python

Это основная часть туториала. Главная идея, которая делает всё более устойчивым: не используй CSS-селекторы, а извлекай скрытый JSON. Yelp рандомизирует названия CSS-классов на этапе сборки (y-css-14xwok2 на этой неделе, y-css-hcq7b9 на следующей), поэтому любой скрапер, завязанный на них, ломается за несколько недель. Встроенные JSON-payload’ы — application/ld+json и react-root-props — остаются стабильными.

Шаг 2: парсинг результатов поиска Yelp

URL поиска Yelp имеет предсказуемый формат: https://www.yelp.com/search?find_desc={term}&find_loc={location}. Данные результатов поиска встроены в тег <script data-id="react-root-props"> как JSON, а не размазаны по 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"Блокировка на странице {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"На странице {page} не найден react-root-props — возможна мягкая блокировка")
            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

На выходе ты получишь список словарей с названиями компаний, URL, рейтингами и количеством отзывов. Если react-root-props отсутствует в ответе, тебе показали блок-оболочку — меняй IP и повторяй запрос.

Заголовок Cookie: intl_splash=false — стандартный обход редиректа с выбором страны у Yelp. Без него IP не из США попадают на splash-страницу, которая выглядит как мягкая блокировка, хотя ею не является.

Шаг 3: парсинг страниц компаний Yelp

Каждый URL компании из результатов поиска ведёт на карточку с более богатым набором данных. Самая стабильная цель для извлечения — блок <script type="application/ld+json">: там лежат структурированные schema.org-данные, которые Yelp поддерживает ради SEO и не пытается обфусцировать.

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}

Значение meta[name="yelp-biz-id"] — это закодированный ID компании, который понадобится для endpoint с отзывами. Сохрани его здесь — он пригодится на следующем шаге.

Шаг 4: парсинг отзывов Yelp с пагинацией

Вот здесь Fusion API уступает, а scraping выигрывает. Внутренний GraphQL batch-endpoint Yelp возвращает полный текст отзывов, информацию об авторе, даты, рейтинги и количество голосов — всё то, что скрывает API.

Endpoint находится по адресу https://www.yelp.com/gql/batch, и для операции GetBusinessReviewFeed используется статический documentId. Пагинация строится через cursor, закодированный в 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"Ошибка при получении отзывов на смещении {offset}: {r.status_code}")
            break
        data = r.json()
        # Переходим по структуре ответа и извлекаем отзывы
        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

Каждая страница возвращает 10 отзывов. Для пагинации увеличивай offset в cursor, закодированном в base64. Параметр sortBy принимает DATE_DESC (сначала новые), RATING_ASC, RATING_DESC и другие значения.

Шаг 5: экспорт собранных данных Yelp

import pandas as pd

# Предположим, что у тебя уже собраны businesses и reviews
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)

# Или сохрани в JSON для большей гибкости
import json
with open("yelp_data.json", "w") as f:
    json.dump({"businesses": businesses, "reviews": all_reviews}, f, indent=2)

Для тех, кто идёт по no-code пути, Thunderbit экспортирует те же данные прямо в Excel, Google Sheets, Airtable или Notion — без pandas и без кода для записи файлов.

Антиблокировочная стратегия: как парсить Yelp и не ловить блокировку

Именно ради этого раздела статья и написана. Меры защиты Yelp от ботов с конца 2024 года стали заметно жёстче — TLS fingerprinting, проверка репутации IP, CAPTCHA и поведенческий анализ теперь используются одновременно. Большинство старых гайдов устарели, потому что были написаны до этой волны ужесточения.

yelp_antiblock_518f0447bb.png

Стратегия должна быть многоуровневой. Каждый уровень снижает шанс блокировки; вместе они делают устойчивый парсинг реальным.

Уровень 1: реалистичные заголовки запроса

Стандартные заголовки Python requests отправляют User-Agent: python-requests/2.x — и это блокируется сразу. Но даже реалистичного User-Agent недостаточно. Yelp проверяет полный набор заголовков Client Hints на согласованность.

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",
}

Три ошибки, из-за которых тебя быстро вычисляют:

  1. UA говорит, что это Chrome, но sec-ch-ua отсутствует или противоречит версии браузера
  2. sec-ch-ua-platform говорит "Windows", а в строке UA указан macOS
  3. Один и тот же UA на тысячи запросов с одного IP — нужен пул из 10–20 свежих строк Chrome/Firefox/Safari

Уровень 2: rate limiting и случайные задержки

Предсказуемые интервалы — красный флаг. Добавляй случайные паузы и exponential backoff при ошибках.

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"Блокировка после {attempt + 1} попыток на {url}")
        backoff = 2 ** (attempt + 1) + random.random()
        print(f"  Получен {r.status_code}, делаю паузу {backoff:.1f}с (попытка {attempt + 1})")
        time.sleep(backoff)
        return polite_get(client_get, url, attempt + 1)
    time.sleep(random.uniform(3, 7))
    return r
ПараметрРекомендуемое значение
Случайная пауза между запросамиrandom.uniform(3, 7) секунд
Backoff при 429/403/5032 → 4 → 8 → 16 сек, максимум 5 попыток
Одновременные воркеры на один IP1 (сериализуйте трафик на IP; для параллельности используйте прокси)
Максимальная устойчивая скорость на residential IPпримерно 1 запрос / 5 сек (~12 rpm)

Уровень 3: ротация User-Agent и сессий

Используй пул реальных browser User-Agent. Сохраняй сессии и cookies, чтобы имитировать обычное поведение пользователя — Yelp применяет cookie-based detection, поэтому новая сессия на каждый запрос тоже выглядит подозрительно.

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",
    # Добавьте ещё 5–10 свежих строк
]

Уровень 4: ротация прокси

При любом серьёзном объёме тебе нужны residential proxies. Датацентровые и бесплатные прокси для Yelp не работают — IP-слой репутации Yelp заранее отдаёт 403 для адресов AWS, GCP и DigitalOcean.

ПровайдерСтартовая цена за GBПримечания
IPRoyal$1.75/GBСамый дешёвый; на нём работает самый цитируемый туториал по Yelp
Decodo (бывш. Smartproxy)$3.20–$3.50Лучшее соотношение GB/цена при большом объёме
Bright Data$4.00 (PAYG)Пул из 150M+ IP; есть отдельная страница Yelp Proxies
Oxylabs$6.00–$8.00Премиум-уровень; 10M+ IPs
Aluvia (mobile SIM)$3.00Реальные мобильные IP от американских операторов, позиционируются под Yelp

Ротационные residential proxies (новый IP на каждый запрос) лучше всего работают для больших поисковых обходов. Sticky sessions (один IP на 10 минут) лучше подходят, когда нужно сохранить cookies на цепочке страница компании → отзывы → пагинация.

Уровень 5: как распознавать и обрабатывать блокировки

Не каждая блокировка выглядит одинаково. Yelp часто отдаёт не CAPTCHA, а общий shell с текстом вроде "page not available", из-за чего наивные скраперы думают, что получили данные, хотя на самом деле это пустой ответ.

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
    # Если это search/business page, но react-root-props отсутствует,
    # Yelp прислал урезанный блок-ответ
    if "react-root-props" not in body and "/biz/" in str(resp.url):
        return True
    return False
СигналЧто это значит
HTTP 403Жёсткая блокировка — IP/заголовки/TLS уже сгорели
HTTP 429Ограничение по частоте — часто можно восстановиться backoff’ом
HTTP 503Общая блокировка или перегрузка
Редирект на /error или тело с "page not available"Мягкая блокировка
Пустой только со Challenge page, ожидающая JS
captcha / g-recaptcha / px-captcha в телеЭскалация — требуется CAPTCHA
Отсутствует react-root-props на странице спискаУрезанный блок-ответ

Уровень 6: устойчивый способ парсинга — скрытый JSON вместо CSS-селекторов

Повторю ещё раз: Yelp рандомизирует CSS-классы на этапе сборки. Скрапер, завязанный на h3.y-css-14xwok2, сломается за несколько недель, когда Yelp выкатит новую сборку с h3.y-css-hcq7b9.

Пейлоады, которые не меняются:

  • <script type="application/ld+json"> — структурированные schema.org-данные (название, адрес, телефон, рейтинг, часы)
  • <script data-id="react-root-props"> — полные данные результатов поиска в JSON
  • https://www.yelp.com/gql/batch — GraphQL endpoint с отзывами и стабильным documentId

Если ты парсишь CSS-классы, ты строишь на песке. Парси JSON.

Уровень 7: запасной вариант со stealth-браузером

Переходи к headless-браузеру только если curl_cffi + residential proxies не проходят — обычно это бывает, когда Yelp показывает JavaScript challenge page или CAPTCHA.

Для 95% задач по парсингу компаний, поисковых результатов и отзывов curl_cffi + скрытый JSON + residential proxies быстрее, дешевле и надёжнее браузера. Но если браузер всё же нужен:

ИнструментСтатус (2025)Примечания
rebrowser-playwrightЛучший стартовый вариантГотовый Playwright с патчами против утечек CDP
nodriverЛучший stealth для ChromeНаследник undetected-chromedriver; полностью обходит WebDriver protocol
patchrightАктивно поддерживаемый форк PlaywrightПроходит современные тесты на детект
playwright-stealthЗрелое решениеПатчит navigator.webdriver, убирает HeadlessChrome из UA

Для Yelp не используй обычный Selenium. Его слишком легко распознать.

Yelp Fusion API vs. Python scraping vs. Thunderbit: полное сравнение

ПараметрYelp Fusion APIPython scrapingThunderbit
Полный текст отзывов❌ 3 фрагмента × ~160 символов✅ Без ограничений (GraphQL)✅ Встроенный шаблон для отзывов
Метаданные отзывов (голоса, ответы владельца)✅ Через поля, предложенные ИИ
Фото❌ (0 в Base)✅ Без ограничений
Макс. результатов в поиске240 (раньше было 1 000 до 2024)Без ограничений (с пагинацией)Без ограничений
Дневной лимит300–500 (новые) / 5 000 (старые)Только бюджет на проксиПо кредитам (3 000/мес на Pro)
Сложность запуска~15 минутОт часов до дней~2 минуты
Обход антиботаНе нуженВаша задачаОбрабатывается (Cloud mode)
Юридический рискНизкий (официальный доступ)Средний (серая зона ToS)Средний (тот же риск, что у парсинга)
Стоимость на стартеот $29/мес~$0.75–$4/GB на прокси + время разработчикаБесплатный тариф
Стоимость при активном использовании$643+/мес$50–$500/мес на прокси + время разработчика$38–$49/мес
Экспорт данныхJSONCSV/JSON (если напишете сами)Excel / Sheets / Airtable / Notion — бесплатно
ПоддержкаНизкаяВысокая (селекторы ломаются, антибот усиливается)Низкая (ИИ подстраивается)

Юридические и этические рекомендации по парсингу Yelp

Я не юрист, и это не юридическая консультация. Но за последние два года правовая картина изменилась настолько, что базовые вещи стоит понимать до того, как ты вложишь время в проект по парсингу Yelp.

Что говорит пользовательское соглашение Yelp: обновление ToS от октября 2025 года прямо запрещает использовать "any robot, spider... or other automated device" для того, чтобы "access, retrieve, copy, scrape, or index any portion of the Service." Там же добавили формулировки про "AI Technologies and/or other automated tools."

Служба поддержки Yelp подтверждает: "Yelp не разрешает никакой парсинг сайта."

Что говорит robots.txt: в robots.txt Yelp стоит wildcard User-agent: * / Disallow: /, а также отдельно блокируются GPTBot, ClaudeBot, PerplexityBot, CCBot и Meta-ExternalAgent. В белом списке только Googlebot, Bingbot и несколько социальных crawlers.

Важный судебный прецедент: в деле Meta v. Bright Data (N.D. Cal., январь 2024) суд постановил, что парсинг общедоступных данных без входа в аккаунт не нарушал ToS Meta. Ключевое различие здесь такое: публичные данные без авторизации vs. данные за логином. Дело hiQ v. LinkedIn показало, что парсинг публичных данных, вероятно, не нарушает CFAA, но при этом hiQ всё равно проиграл по искам на уровне штата (trespass to chattels, misappropriation) и получил решение на $500 000.

Практические рекомендации:

  • Парси только общедоступные страницы без авторизации
  • Ограничивай частоту запросов (задержки в этом гайде одновременно работают и как этический rate limit)
  • Не перепродавай сырой текст отзывов с привязкой к именам пользователей — уважай приватность авторов
  • Соблюдай местные законы о защите данных (CCPA, GDPR)
  • Не входи в аккаунт ради парсинга — это уже выход за рамки разрешённого доступа
  • Считай бизнес-данные (название/адрес/телефон/рейтинг) публичными фактами, а текст отзывов — более чувствительной информацией

В конкретной ситуации обязательно проконсультируйся с юристом.

Итоги

Три пути, одна цель.

Yelp Fusion API — это официальный и неприхотливый вариант, но он ограничен 3 фрагментами отзывов и стоит от $29 в месяц. Python scraping даёт полный контроль над каждым полем Yelp, но требует серьёзных вложений: curl_cffi для имитации TLS-фингерпринта, residential proxies, случайные задержки, парсинг скрытого JSON и постоянная поддержка по мере эволюции защиты Yelp. Thunderbit позволяет пройти путь от "мне нужны данные Yelp" до "вот моя таблица" примерно за 30 секунд — без кода и без настройки прокси.

Антиблокировочные приёмы, которые реально работают в 2025 году: реалистичные заголовки с полным набором Client Hints, curl_cffi для имитации TLS-фингерпринта, случайные задержки с exponential backoff, ротация residential proxies и — самое главное — парсинг скрытого JSON (application/ld+json и react-root-props) вместо хрупких CSS-селекторов.

Не уверен, что подойдёт именно тебе? Сначала попробуй бесплатный тариф Thunderbit. Если он закроет задачу, ты сэкономишь часы. Если нужен больший контроль — полноценные программные пайплайны, кастомные поля, тесная интеграция с CRM — тогда подойдёт Python-гайд выше. А если хочешь глубже изучить рынок инструментов для парсинга, посмотри наш обзор лучших расширений Chrome для web scraper или руководство по извлечению данных с сайтов в Excel.

Попробовать Thunderbit для извлечения данных Yelp Get Started Free

Частые вопросы

Можно ли бесплатно парсить Yelp на Python?

Да — с помощью бесплатных библиотек вроде curl_cffi, parsel и jmespath. Но при любом реальном объёме (больше нескольких десятков страниц) тебе понадобятся платные residential proxies, которые начинаются примерно с $1.75/GB у IPRoyal. У Thunderbit тоже есть бесплатный тариф: 6 страниц в месяц для быстрого no-code извлечения.

Yelp блокирует скраперы?

Да, и довольно агрессивно. Yelp использует TLS/JA3 fingerprinting, оценку репутации IP, CAPTCHA, поведенческий анализ и постоянно меняющиеся обфусцированные CSS-классы. Обычный requests блокируется с первого же запроса. Слоистая антиблокировочная стратегия из этого гайда — curl_cffi для имитации TLS, реалистичные заголовки, случайные задержки и residential proxies — это то, что работает в 2025 году.

Yelp Fusion API лучше, чем парсинг?

Зависит от задачи. API официальный и с низким риском, но он отдаёт только 3 фрагмента отзывов по ~160 символов каждый, ограничивает поиск 240 результатами и стоит от $29 в месяц. Если тебе нужен полный текст отзывов, метаданные или больше нескольких сотен записей в день, парсинг — единственный вариант.

Как парсить отзывы Yelp на Python?

Используй curl_cffi с impersonate="chrome131", чтобы получить страницу компании, затем извлеки закодированный business ID из <meta name="yelp-biz-id"> и отправь POST-запрос на https://www.yelp.com/gql/batch с операцией GetBusinessReviewFeed, используя пагинацию через base64-кодированный cursor after. Пошаговый код есть выше в разделе с туториалом. Репозиторий Scrapfly Yelp scraper repo тоже хороший пример реализации.

Можно ли парсить Yelp без кода?

Да — AI Web Scraper от Thunderbit уже поставляется с готовыми шаблонами для Yelp business и reviews. Открой страницу Yelp, нажми AI Suggest Fields, затем Scrape. Экспорт в Google Sheets, Excel, Airtable и Notion бесплатен на любом тарифе, включая free plan.

Узнать больше

Shuai Guan
Shuai Guan
Генеральный директор Thunderbit | Эксперт по автоматизации данных с помощью ИИ Shuai Guan — генеральный директор Thunderbit и выпускник инженерного факультета Мичиганского университета. Опираясь почти на десятилетний опыт в сфере технологий и SaaS-архитектуры, он специализируется на том, чтобы превращать сложные модели ИИ в практичные no-code инструменты для извлечения данных. В этом блоге он делится честными, проверенными в деле инсайтами о веб-скрейпинге и стратегиях автоматизации, чтобы помочь вам выстраивать более умные, data-driven рабочие процессы. Когда он не занимается оптимизацией потоков данных, тот же внимательный подход к деталям он переносит в своё увлечение фотографией.
Содержание

Собирай страницу, просто задав вопрос

Скажи, что тебе нужно, простыми словами. А ещё лучше — ничего не говори.

Попробовать Thunderbit бесплатно
Извлекай данные с помощью ИИ
Легко передавай данные в Google Sheets, Airtable или Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week