Yelp
URL шаблон: https://www.yelp.com/biz/{alias}
Источник данных: ScraperAPI render=true → HTML с Apollo cache (HTML-comment в <script data-apollo-state>)
Стратегия парсинга
- GET главной страницы биза через ScraperAPI render=true.
- В HTML находим `<script type="application/json" data-apollo-state="..."><!--{...}--></script>` — Apollo state в HTML-entity-escaped JSON.
- Декодируем `"` через `html.unescape`, парсим JSON, получаем граф объектов: `Business:...`, `BusinessLocation:...`, `Review:...`, `BusinessPhoto:...` и т.д.
- Извлекаем `Business:...` объект → mapping в нашу структуру (резолвим `__ref` ссылки).
- Пагинируем отзывы по `?start={N}&sort_by=date_desc` — каждый запрос = новый ScraperAPI вызов. Останавливаемся, когда пришли только дубликаты.
Поля ресторана
| поле | откуда / как | пример |
|---|---|---|
encid / alias | Business.encid / Business.alias | '_kdL...' / 'ghirardelli-ice-cream-...' |
name | Business.name | 'Ghirardelli Ice Cream...' |
rating | Business.rating(...) (parameterized) | 4.0 |
review_count | Business.reviewCount | 721 |
price_range | Business.priceRange.display | '$$' |
categories | Business.categories[].title (через __ref) | ['Ice Cream & Frozen Yogurt','Desserts'] |
address fields | BusinessLocation.address | '2 New Montgomery St' |
city / state / postal_code / country | BusinessLocation.address + .country | 'San Francisco / CA / 94105 / US' |
neighborhoods | BusinessLocation.neighborhoods | ['SoMa'] |
coordinates | из map(...).src — center=lat,lng | {lat:37.78, lng:-122.40} |
phone | Business.phoneNumber.formatted | '(415) 397-3030' |
website | Business.externalResources.website.url | 'https://ghirardelli.com' |
menu_url | Business.externalResources.menu.url | null или url |
hours_today / hours_week | Business.operationHours.regularHoursMerged... | '08:00 AM - 11:00 PM' |
specialties / history | Business.specialties / .history | 'We make chocolate since 1852…' |
is_closed | Business.isClosed | false |
is_claimed | Business.claimability.isClaimed | true |
is_advertiser | Business.isYelpAdvertiser | false |
biz_photos | все BusinessPhoto:* объекты с photoUrl(LARGE) | [{url, caption, width, height}, ...] |
Поля отзыва
| поле | откуда / как | пример |
|---|---|---|
encid | Review.encid | 'A1B2C3...' |
rating | Review.rating | 5 |
text | Review.text.full / .truncated | 'Great service...' |
created_at | Review.createdAt.utcDateTime | '2025-12-01T...' |
author_name | User.displayName (через __ref) | 'John D.' |
author_encid | User.encid | строка |
author_elite | User.isElite | true/false |
author_photo_url | User.profilePhoto.url(...) | 'https://...yelpcdn.com/...' |
photo_urls | Review.photos.edges[].node (LARGE) | ['https://...'] |
reactions | Review.usefulCount/funnyCount/coolCount | {useful:5, funny:1} |
Пример запроса
{
"platform": "yelp",
"url": "https://www.yelp.com/biz/alias"
}
Опции options.scraperapi описаны в общей документации.
Нюансы
- Yelp иногда меняет обёртку Apollo — мы покрываем 3 формата (текущий + 2 legacy fallback).
- Пагинация: новый Yelp может игнорировать `?start=N` и возвращать те же первые 10 отзывов — тогда мы видим 0 новых и останавливаемся.
- Лимит отзывов: env `YELP_MAX_REVIEWS` (default 500) — обрезаем по этому значению, чтобы не пожечь кредиты ScraperAPI.
- Фотографии biz и review приходят как URL с yelpcdn.com — не скачиваем, только URL.