Python で Yelp をブロックされずにスクレイピングする方法

最終更新日 June 9, 2026
Python で Yelp をブロックされずにスクレイピングする方法

累計3億3,000万件のレビューに、840万件のアクティブな店舗ページ。Yelp に蓄積されたこの情報量は、ローカルビジネスの宝の山です。ところが近頃は、それを使える形に持っていくこと自体が、年々骨の折れる作業になってきました。元凶は、2024年から2025年にかけて段階的に強化された Yelp の対ボット防御。世に出回る Python スクレイピングの解説記事は、その多くがこの締め付けより前に書かれたもので、気づけば軒並み役に立たなくなっているのです。

最近 Yelp 向けのスクレイパーを動かして、403 が返ってきたり、HTML が空っぽだったり、半年前には見かけなかった CAPTCHA に行き当たったり——もしそんな経験をしたなら、あなたの腕が落ちたわけではありません。今の Yelp は、TLS/JA3 フィンガープリント、ビルドのたびに名前が変わる難読化済み CSS クラス、そして手厳しい IP レピュテーション判定を、まとめて投入しています。要するに、いまだ多くのチュートリアルが勧めている requests + BeautifulSoup のやり方は、最初の1リクエストであっさり跳ね返されるわけです。私はここ数週間、Yelp の現行スタックに対してあれこれ手法をぶつけて検証してきました。この記事に載せたのは、2025年の Yelp で実際に通る方法だけです。公式の Fusion API がなぜ単独では力不足なのか、多層のブロック回避を仕込んだ Python の実戦ワークフロー、そしてデバッグ地獄を回避したい人向けに Thunderbit を使った2クリックのノーコード代替——この三本立てで進めます。

Yelp スクレイピングで Thunderbit を試す

なぜ Python で Yelp をスクレイピングするのか、誰に向いているのか

手を動かしてコードを書く前に、そもそも Yelp のデータで何を成し遂げたいのかを、はっきりさせておきましょう。Yelp は飲食店の口コミサイト、というだけの存在ではありません。店舗の連絡先、評価、カテゴリ、営業時間、さらに数億件規模の顧客レビューまで抱えた、実質的なローカルビジネスのライブデータベースなのです。

yelp_stats_bd6a43108e.png

とりわけ旨味が大きいのは、次のような場面です。

用途主な取得項目重要な理由
営業・リード獲得店名、電話番号、Web サイト、住所、カテゴリ、評価地域の中小企業を狙った見込み客リストを作成できる — Yelp ユーザーの 5 人中 4 人は来店準備ができている
競合分析レビュー、星評価、レビュー件数、感情傾向競合の評判を追跡し、サービスの穴やトレンドを把握できる
市場調査・NLPレビュー本文、日付、レビュアー情報感情分析やトピックモデリングに活用できる — Yelp レビューは学術研究で 最もよく使われる NLP コーパスのひとつ
不動産・出店戦略業種密度、カテゴリの偏り、地域ごとのレビュー品質フランチャイズや小売店の出店判断に有効 — Yelp 自身もこの用途向けに Location Intelligence を提供している
EC・運営改善価格の傾向、顧客の不満、営業時間競合の評価傾向や運営上のパターンを把握できる

どの用途にも共通しているのは、ゴールが「構造化データ」だという点です。Python は、そこへたどり着くための一手段にすぎません。コードで隅々まで制御したい人もいれば、オースティンの配管業者の連絡先がまとまった表が一枚あれば十分、という人もいるでしょう。この記事では、その両方をカバーします。

Yelp Fusion API と Python スクレイピング、どちらを使うべきか?

たいていの解説は、ここの判断をすっ飛ばして、いきなりコードへ突入します。けれども、公式の Yelp Fusion API(今は「Yelp Places API」へと改称されています)でそもそも事足りたのではないか——この見極めこそ、何より大切です。私の経験では、ここを最初に押さえておくだけで、後々の何時間もの空回りを防げます。API は得意な用途では実に優秀ですが、苦手な用途ではまるで歯が立たないからです。

Fusion API で実際に取れるもの

Fusion API には、構造化されたビジネス検索、詳細情報、オートコンプリート、そしてレビュー用のエンドポイントが揃っています。公式に認められた手段で、ドキュメントもしっかりしており、対ボット対策にあれこれ頭を悩ませる必要も基本的にありません。

引っかかるのは、レビュー用エンドポイントです。Yelp のスタッフが GitHub 上でこう言い切っています。

「Yelp API はレビュー本文の全文を返しません。デフォルトでは 160 文字のレビュー抜粋が 3 件提供されます。」 — Yelp スタッフの返信, GitHub Issue #163

これはバグではなく、れっきとした仕様です。API で取れるのは物理的に3件のレビュー抜粋まで(Premium でも7件止まり)で、各抜粋はおよそ160文字で打ち切られます。レビューのメタデータ(役に立った・面白い・クール)も、レビュアーの履歴も、オーナーの返信も手に入りません。おまけに 新規クライアントの1日あたり上限は2023年5月以降300〜500回まで引き下げられ、かつての5,000回から大きく後退しました。料金は $29/月 から始まります。

判断の目安

要素Yelp Fusion APIPython スクレイピングThunderbit(ノーコード)
全レビュー❌ 3 件の抜粋のみ(各約 160 文字)✅ GraphQL 経由で全件取得✅ 表示されているレビューをすべて取得
レート制限300〜500/日(新規); 5,000(旧)自己管理(プロキシ予算次第)クレジット制
初期設定の手間約15分(API キー + SDK)数時間〜数日約2分
取得できるビジネス項目約20項目の構造化データ制限なし(HTML/JSON を解析)AI が項目を提案
対ボット対応不要(公認)自作が必要自動対応
法的リスク✅ 公認⚠️ 利用規約上グレー⚠️ スクレイピング同様
コスト最低 $29/月無料 + プロキシ費用 $0.75〜$4/GB無料プランあり
保守低い(API が安定)高い(セレクタが壊れ、対策が強化され続ける)低い(AI が再適応)

Fusion API が向いているケース: 基本的な店舗情報、小規模検索、公認された連携が必要で、1 店舗あたりレビュー抜粋 3 件で十分な場合。

Python スクレイピングが向いているケース: レビュー全文、1 店舗の全レビュー、レビューのメタデータ、1 回の検索で 240 件以上、または予算が月 $29 未満の場合。

Thunderbit が向いているケース: コードを書かず、保守もせず、とにかく速くデータを取りたい場合。詳細は下のノーコード章で説明します。

ノーコードの近道: Thunderbit で Yelp をスクレイピングする(Python 不要)

Python の深みに分け入る前に、「コードを書くこと」ではなく「データを手に入れること」が目的の読者へ、いちばんの近道を案内します。同種の記事の多くは Python が書ける前提で語りますが、Thunderbit で数多くのユーザーと接してきた実感を言えば、「Yelp をスクレイピングしたい」と検索する人のほとんどは、営業担当、オペレーション担当、あるいは中小企業のオーナーです。TLS フィンガープリントの講義を聴きたいわけではなく、地元の事業者一覧を表計算ソフトに収めたい——ただそれだけなのです。

Thunderbit には、Yelp 専用のテンプレートが最初から備わっています。

  • Yelp Business Web Scraper — 店名、評価、連絡先、住所、営業時間、カテゴリを抽出
  • Yelp Review Scraper — レビュアー名、レビュー本文、評価、日付、レビュアー所在地を抽出

実際の使い方

  1. Chrome で Yelp の検索結果ページまたは店舗ページを開く
  2. Thunderbit 拡張機能AI Suggest Fields をクリックする — AI がページを読み取り、列を提案する(店名、評価、レビュー件数、価格帯、カテゴリ、住所、電話番号、URL など)
  3. Scrape をクリックする — これで完了

あらかじめ用意された Yelp テンプレートを使えば、手間はさらに減ります。テンプレートを開いて Scrape を押す。それだけです。

サブページスクレイピング をオンにすれば、詳細情報の埋め合わせも自動でこなしてくれます。Yelp の検索結果ページを出発点にこの機能を有効にすると、Thunderbit が各店舗ページを順に巡り、営業時間、レビュー全文、Web サイト、写真、設備情報などを集めてきます。追加の設定は一切いりません。

ページネーションも自動でこなします。 クリック型とスクロール型、どちらの方式にも最初から対応済みです。(仕組みを詳しく知りたい方は、ページネーションのガイド をどうぞ。)

エクスポートはどのプランでも無料 で、Excel、Google Sheets、Airtable、Notion、CSV、JSON へ書き出せます。pandas も、CSV 出力用のコードも必要ありません。

時間比較

所要時間Python スクレイパーThunderbit
初回実行数時間〜数日(セレクタ作成、ページネーション、プロキシ、リトライ処理)あらかじめ用意された Yelp テンプレートで約30秒
Yelp が DOM を変更したときセレクタを手動で書き直すAI Suggest Fields をもう一度押すだけで自動再適応
IP がブロックされたときデバッグ、プロキシプールの切り替え、再テストCloud モードが IP ローテーションを処理
Google Sheets へ出力OAuth と pandas の連携コードを書くワンクリック、無料

まず Thunderbit を回してみて、それで要件が満たせるなら、この先は読まずに閉じてしまって構いません。プログラムで全面的に制御したい、独自の項目が欲しい、月に数千件を超えるスケールで扱いたい——そういう方は、このまま読み進めてください。

Yelp スクレイピングに使う Python ライブラリはどれを選ぶべきか

「Scrapy か、BS4+requests か、それとも Selenium か?」——Yelp 絡みの r/webscraping のスレッドでは、お決まりのように飛び交う問いです。ところが多くのチュートリアルは、お気に入りのライブラリをひとつ選んで終わり、なぜそれが適しているのかには触れません。ここでは、忖度なしに整理します。

2025 年の現実: requests + BeautifulSoup は Yelp では使えない

Yelp 解説の定番として必ず登場する pip install requests beautifulsoup4 の組み合わせ。これは2025年の Yelp では、最初のリクエストで弾かれます。50回目ではなく、1回目で、です。

理由は、Python の requests ライブラリが、本物のブラウザとは噛み合わない TLS/JA3 フィンガープリントを送り出してしまうから。Yelp の対ボット層は、User-Agent ヘッダーを読むより前、TLS ハンドシェイクの段階でそれを見抜きます。私はこれを繰り返し検証しました。新しい IP を使い、それらしいヘッダーを添え、ランダムな遅延を挟んでも、素の requests はあっという間に 403 Forbidden を返してきました。

ライブラリの比較

ライブラリ向いている用途JS 対応対ボット対策学習コスト速度
requests + BeautifulSoup単一ページの簡単なスクレイピング(Yelp では不可)とても低い速い(ブロックされるまで)
httpx async + parsel大規模な非同期スクレイピング低い非常に速い
curl_cffi + parselYelp 特化: TLS なりすまし✅ TLS/JA3/HTTP2低い非常に速い
Scrapy 2.14ページネーションを含むフルクロール一部対応(scrapy-playwright 経由)AutoThrottle、retry ミドルウェア中〜高速い
Selenium 4.43 / Playwright 1.58JS が多いページ、CAPTCHA 回避部分的遅い(約10〜30ページ/分)
Thunderbit非エンジニア、素早い抽出✅(ブラウザ)内蔵(Cloud モード)とても低い速い

curl_cffi が変えたこと

私の Yelp スクレイピングを根本から塗り替えたのが curl_cffi でした。curl-impersonate の Python バインディングで、本物の Chrome と寸分違わぬ TLS/JA3 + HTTP/2 フィンガープリント を送出してくれます。しかも 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 か、その分かれ目がまさにここにありました。

2025 年に Yelp 向けで推奨する構成: curl_cffi + parsel + jmespath + レジデンシャルプロキシ。スケジューリングを含むフルクロールパイプラインが必要なら、curl_cffi ベースの downloader middleware を組み込んだ Scrapy 2.14 で包むとよいです。

Yelp をスクレイピングするための Python 環境構築

  • 難易度: 中級
  • 所要時間: セットアップ 15 分程度、動くスクレイパー完成まで 1〜2 時間
  • 必要なもの: Python 3.10 以上(推奨は 3.12)、ターミナル、必要に応じてレジデンシャルプロキシ業者

手順 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 — Chrome の TLS フィンガープリントで HTTP リクエストを送る(対ボット回避の要)
  • parsel — HTML を解析するための CSS/XPath セレクタ(Scrapy と同じエンジンで軽量)
  • jmespath — 宣言的な JSON クエリ(Yelp の埋め込み JSON を扱うときにネストした辞書アクセスより読みやすい)
  • pandas — CSV/Excel へのデータ出力

あると便利なもの:

pip install fake-useragent  # 注: リポジトリは 2026 年 4 月にアーカイブされたが、今でもインストール可能

手順を追って解説: Python で Yelp をスクレイピングする方法

いよいよ本編です。すべてを格段に壊れにくくする肝は、CSS セレクタを追いかけるのをやめ、隠れた JSON を直接つかみにいくことにあります。Yelp はビルドのたびに CSS クラス名をランダムに振り直します(今週は y-css-14xwok2、来週は y-css-hcq7b9、という調子で)。だからそこへ依存したスクレイパーは、ものの数週間で崩れます。対照的に、埋め込み JSON ペイロードである application/ld+json スキーマや react-root-props は、びくともしません。

手順 2: Yelp の検索結果を取得する

Yelp の検索 URL は https://www.yelp.com/search?find_desc={term}&find_loc={location} という、見通しのよい形をしています。肝心の検索結果データは、CSS クラスまみれの HTML ではなく、<script data-id="react-root-props"> タグの中に JSON として埋め込まれています。

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、評価、レビュー件数を収めた dict のリストが返ってくるはずです。もしレスポンスから react-root-props が抜け落ちていたら、それはブロック用の空シェルが返ってきているサインなので、IP を切り替えて試し直してください。

Cookie: intl_splash=false は、Yelp の国別スプラッシュリダイレクトをかわすための定番テクニックです。これを欠くと、米国外の IP ではソフトブロックさながらのスプラッシュページへ飛ばされてしまいます。

手順 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 です。次の手順で使うので、ここで忘れずに取得しておきましょう。

手順 4: ページネーション付きで Yelp のレビューを取得する

ここでこそ、Fusion API の頭打ちが露わになり、スクレイピングが本領を発揮します。Yelp の内部 GraphQL バッチエンドポイントは、レビュー全文、レビュアー情報、日付、評価、投票数までごっそり返してくれます。API が頑として渡さない情報が、ここで一通り手に入るのです。

エンドポイントは https://www.yelp.com/gql/batchGetBusinessReviewFeed オペレーション向けに固定の documentId を使います。ページ送りは base64 エンコードされた cursor で制御します。

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 {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

1 ページにつき 10 件のレビューが返ります。after cursor の中の offset を増やしていけば、次のページへ進めます。sortBy には DATE_DESC(新しい順)、RATING_ASCRATING_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)

ノーコード派の読者なら、Thunderbit が同じデータを Excel、Google Sheets、Airtable、Notion へそのまま吐き出してくれます。pandas も、ファイル書き込みのコードもいりません。

ブロック回避の実践集: Yelp にブロックされずにスクレイピングする方法

この章こそ、この記事を書いた本当の動機です。Yelp の対ボット対策は2024年後半を境にぐっと厳しくなり、TLS フィンガープリント、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",
}

ブロックを招きやすい典型的なミスは、次の3つです。

  1. UA は Chrome を名乗っているのに sec-ch-ua が抜けている、もしくは UA のバージョンと食い違っている
  2. sec-ch-ua-platform は「Windows」なのに、UA 文字列の方は macOS を指している
  3. 1 つの IP から、同じ UA を何千回も使い回している — 最近の Chrome/Firefox/Safari の文字列を 10〜20 個ほど用意してローテーションする

レイヤー 2: レート制御とランダム遅延

きれいに揃ったタイミングは、それ自体が警告灯です。sleep の間隔をばらつかせ、エラー時には指数バックオフを効かせましょう。

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"{url} で {attempt + 1} 回試行後もブロックされました")
        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)
429/403/503 時のバックオフ2 → 4 → 8 → 16 秒、最大 5 回
1 IP あたりの並列ワーカー数1(IP ごとに直列処理し、並列化はプロキシで)
レジデンシャル IP の持続的な最大レート約 1 リクエスト / 5 秒(約 12 rpm)

レイヤー 3: User-Agent とセッションのローテーション

実在するブラウザの User-Agent を複数仕込み、切り替えながら使います。セッションと Cookie はそのまま保ち、生身のブラウジングに近い振る舞いを演出します。Yelp は Cookie ベースの検出をかけてくるので、リクエストのたびに新しいセッションを立てるのは、かえって不自然に映ります。

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_1; 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: プロキシのローテーション

実運用レベルの量をさばくなら、レジデンシャルプロキシが欠かせません。データセンター系や無料のプロキシは、Yelp では通用しません。Yelp の IP レピュテーション層は、AWS、GCP、DigitalOcean の IP 帯を先回りして 403 で叩き落とします。

提供元開始価格/GB補足
IPRoyal$1.75/GB最安クラス。最も引用される Yelp 解説で使われている
Decodo(旧 Smartproxy)$3.20〜$3.50大量利用時の GB/$ 比が優秀
Bright Data$4.00(従量課金)1.5 億超の IP プール、Yelp 専用ページあり
Oxylabs$6.00〜$8.00プレミアム。 1,000 万超の IP
Aluvia(モバイル SIM)$3.00実在する米国キャリアのモバイル IP。 Yelp 向けとして位置付け

ローテーション型レジデンシャル(リクエストごとに新 IP)は、大量の検索クロールに最適です。スティッキーセッション(10 分ほど同じ IP を維持)は、店舗ページ → レビュー → ページネーションの流れで Cookie を引き継ぎたいときに向いています。

レイヤー 5: ブロックの検知と対処

ブロックの現れ方は、一様ではありません。Yelp は CAPTCHA を出すとは限らず、ありふれた「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
    # これは検索/店舗ページなのに 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レート制限 — バックオフで回復可能なことが多い
HTTP 503一般的なブロック、または負荷切り捨て
/error へリダイレクト、または本文に "page not available"ソフトブロック
が空で JS 待ちのチャレンジページ
本文に captcha / g-recaptcha / px-captcha があるエスカレート済み — CAPTCHA 必要
一覧ページで react-root-props が欠けている中身を抜いたブロック応答

レイヤー 6: もっとも堅牢な解析のコツ — CSS セレクタではなく隠れた JSON を読む

念を押しておきます。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 — 安定した documentId を使う GraphQL レビューエンドポイント

CSS クラスを解析している限り、それは砂地に家を建てるのと同じです。そうではなく、JSON を読みにいってください。

レイヤー 7: ステルスブラウザへのフォールバック

curl_cffi + レジデンシャルプロキシでも突破できないとき——そのときに限って、ヘッドレスブラウザへ切り替えます。だいたいは、Yelp が JavaScript チャレンジページや CAPTCHA を突きつけてくるケースです。

ビジネスページ、検索結果、レビュー取得の 95% は、curl_cffi + 隠れ JSON + レジデンシャルプロキシの組み合わせの方が、ブラウザより速く、安く、そして安定しています。それでもブラウザがどうしても要るなら、候補は次のとおりです。

ツール2025 年の状況備考
rebrowser-playwright最初の推奨候補CDP 漏れを修正した Playwright のドロップイン版
nodriverChrome のステルスに最有力undetected-chromedriver の後継。WebDriver プロトコル自体を回避
patchright継続的に保守されている Playwright フォーク最新の検知テストにも通りやすい
playwright-stealth成熟しているnavigator.webdriver をパッチし、UA から HeadlessChrome を除去

Yelp に vanilla Selenium は使わないでください。フィンガープリントされやすすぎます。

Yelp Fusion API vs. Python スクレイピング vs. Thunderbit: 徹底比較

観点Yelp Fusion APIPython スクレイピングThunderbit
レビュー全文❌ 約160文字の抜粋 3 件✅ 無制限(GraphQL)✅ レビュー用テンプレート内蔵
レビューのメタデータ(投票、オーナー返信)✅ AI で項目提案
写真❌(Base では 0)✅ 無制限
検索あたりの最大件数240(2024 年以前は 1,000)無制限(ページネーションあり)無制限
1 日のレート制限300〜500(新規)/ 5,000(旧)プロキシ予算のみクレジット制(Pro では月 3,000)
初期設定の手間約15分数時間〜数日約2分
対ボット対応不要自分で対応対応済み(Cloud モード)
法的リスク低い(公認)中程度(利用規約上グレー)中程度(スクレイピング同様)
コスト(導入)$29/月約 $0.75〜$4/GB のプロキシ + 開発工数無料プラン
コスト(大量利用)月 $643 以上月 $50〜$500 のプロキシ + 開発工数月 $38〜$49
データ出力JSONCSV/JSON(自分で実装)Excel / Sheets / Airtable / Notion — 無料
保守低い高い(セレクタが壊れ、対策が強化される)低い(AI が再適応)

Yelp スクレイピングの法務と倫理について

私は弁護士ではありませんし、これは法的助言でもありません。とはいえ、この2年で法的な風向きはずいぶん変わったので、Yelp スクレイピングに時間を注ぎ込む前に、最低限の前提は押さえておくべきです。

Yelp の利用規約では何が禁止されているか: 2025 年10月の ToS 更新 では、「ロボット、スパイダー、その他の自動化された手段」を用いて「サービスの一部へアクセス、取得、コピー、スクレイプ、インデックス化すること」が、はっきりと禁じられています。あわせて、「AI Technologies およびその他の自動ツール」に言及する条項も新たに加わりました。

Yelp サポートも、繰り返しこう案内しています——「Yelp はサイトのいかなるスクレイピングも許可していません。」

robots.txt の内容: Yelp の robots.txt には、ワイルドカードの User-agent: * / Disallow: / が置かれており、GPTBot、ClaudeBot、PerplexityBot、CCBot、Meta-ExternalAgent も名指しでブロックしています。通しているのは Googlebot、Bingbot、それと一部のソーシャルメディアクローラーだけです。

重要な判例: Meta v. Bright Data(カリフォルニア北部地区、2024年1月)では、公開かつログアウト状態のデータをスクレイピングしても Meta の利用規約違反には当たらない、と判断されました。ここで効いてくるのが、ログアウト状態の公開データと、ログインを要するデータの線引きです。hiQ v. LinkedIn では、公開データのスクレイピングは CFAA に違反しない公算が高いとされた一方、hiQ は州法上の不法行為(動産侵害、不正流用)で敗れ、50 万ドルの判決を受けました。

実務上の指針:

  • 公開されているログアウト状態のページだけを対象にする
  • リクエスト頻度を抑える(このガイドの遅延処理は、倫理的なレート制限としても機能します)
  • 実名ユーザーに紐づくレビュー本文をそのまま再販しない — 投稿者のプライバシーを尊重する
  • 地域のデータ保護法(CCPA、GDPR)を遵守する
  • ログインしてのスクレイピングはしない — それは許可の境界を越えます
  • 店舗情報(店名、住所、電話番号、評価)は公開された事実データとして扱い、レビュー本文はより慎重に扱う

個別の状況については、法律の専門家に相談してください。

まとめ

ゴールは一つ、たどり着く道は三つです。

Yelp Fusion API は、公認されていて保守の手間も軽い選択肢ですが、取れるレビュー抜粋は3件まで、料金は月 $29 からです。Python スクレイピングを選べば、Yelp 上のありとあらゆるデータを思いどおりに握れます。その代償として、TLS なりすまし用の curl_cffi、レジデンシャルプロキシ、ランダム遅延、隠れ JSON の解析、そして Yelp の防御が進化し続けるなかでの継続的な保守が、すべてのしかかってきます。一方の Thunderbit なら、コードもプロキシ設定も抜きで、「Yelp データが欲しい」から「スプレッドシート完成」まで、わずか30秒ほどで駆け抜けられます。

2025年に本当に効くブロック回避策とは、フルの Client Hints を含む現実的なヘッダー、TLS フィンガープリントを偽装する curl_cffi、指数バックオフを伴うランダム遅延、レジデンシャルプロキシのローテーション、そして何より、壊れやすい CSS セレクタではなく隠れた JSON(application/ld+jsonreact-root-props)を読みにいくこと——これに尽きます。

どの方法が自分に合うか決めかねるなら、まずは Thunderbit の無料プラン を回してみてください。それで要件が満たせれば、何時間分もの手間が浮きます。もっと細やかな制御が要るなら——完全なプログラムパイプライン、独自項目、CRM との緊密な連携など——上の Python ガイドが頼りになります。スクレイピングツール全体を見比べたい向きには、おすすめの Web スクレイパー Chrome 拡張機能 のまとめや、Web サイトのデータを Excel に取り込む方法 のガイドもどうぞ。

Yelp データ抽出で Thunderbit を試す Get Started Free

FAQ

Python で Yelp を無料でスクレイピングできますか?

できます。curl_cffiparseljmespath といった無料ライブラリを組み合わせれば可能です。ただし実運用の規模(数十ページを超えるあたり)になると、IPRoyal で $1.75/GB から といった有料のレジデンシャルプロキシが要ります。手早くノーコードで抜き出したい人向けには、Thunderbit が月6ページまでの無料プランを用意しています。

Yelp はスクレイパーをブロックしますか?

します。しかもかなり本気で。Yelp は TLS/JA3 フィンガープリント、IP レピュテーション、CAPTCHA、行動分析、ローテーションする難読化 CSS クラス を駆使しています。素の requests は最初のアクセスで止められます。このガイドで紹介した、curl_cffi による TLS なりすまし、現実的なヘッダー、ランダム遅延、レジデンシャルプロキシを束ねた多層対策こそが、2025年に機能するやり方です。

Yelp Fusion API はスクレイピングより優れていますか?

用途次第です。API は公認でリスクが低い反面、返してくるのは 約160文字のレビュー抜粋3件 どまり、検索結果も240件まで、料金は月 $29 からです。レビュー全文、レビューのメタデータ、1日に数百件を超える取得が必要なら、スクレイピングが唯一の道になります。

Python で Yelp レビューをスクレイピングするには?

curl_cffiimpersonate="chrome131" 付きで使って店舗ページを取り、<meta name="yelp-biz-id"> からエンコード済みのビジネス ID を抜き出します。続いて https://www.yelp.com/gql/batchGetBusinessReviewFeed オペレーションで POST し、base64 エンコードされた after cursor でページを送ります。手順付きのコードは、上のチュートリアル部分に載せてあります。参考実装としては Scrapfly Yelp scraper repo も役立ちます。

コーディングなしで Yelp をスクレイピングできますか?

できます。Thunderbit の AI Web Scraper には、Yelp ビジネスレビュー のテンプレートが最初から組み込まれています。Yelp のページを開き、AI Suggest Fields を押し、Scrape を押す——それだけです。Google Sheets、Excel、Airtable、Notion への出力は、無料プランを含めどのプランでも無料です。

さらに詳しく

Shuai Guan
Shuai Guan
Thunderbit の CEO | AIデータ自動化のエキスパート Shuai Guan は Thunderbit の CEO であり、ミシガン大学工学部の卒業生です。テクノロジーと SaaS アーキテクチャの分野で約10年にわたる経験をもとに、複雑な AI モデルを、実務で使えるノーコードのデータ抽出ツールへと落とし込むことを得意としています。このブログでは、ウェブスクレイピングや自動化戦略について、実践で磨かれた率直な知見を共有し、より賢くデータ主導のワークフローを構築できるよう支援しています。データワークフローの最適化から離れているときは、同じこだわりと観察眼を写真への情熱にも注いでいます。
目次

ただ伝えるだけで、Webページをスクレイピング

必要なことをそのまま英語で伝えるだけ。いや、何も言わなくてもOKです。

Thunderbitを試す 無料
AIでデータを抽出
Google Sheets、Airtable、Notionへ簡単にデータを移行できます
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week