如何用 Python 抓取 Yelp 而不被封鎖

最後更新於 June 10, 2026
如何用 Python 抓取 Yelp 而不被封鎖

Yelp 目前累積了 3.3 億則評論,分布在 840 萬個活躍商家頁面 上——而且,想把這些資料整理成可用格式,從來沒有現在這麼難。Yelp 在 2024–2025 年加強反機器人措施後,默默地讓大多數現有的 Python 抓取教學失效了。

如果你最近嘗試執行 Yelp 爬蟲,卻一直碰到 403 錯誤、空白 HTML 回應,或是半年前根本不存在的 CAPTCHA,那不是你的錯覺。Yelp 現在會進行 TLS/JA3 指紋辨識、輪換混淆過的 CSS 類名,還會嚴格評估 IP 信譽——這代表教學文還在推薦的 requests + BeautifulSoup 舊方法,第一個請求就會死掉。我花了好幾週測試各種方法對抗 Yelp 現行架構,這篇指南會整理出 2025 年真正有效的做法:官方 Fusion API(以及為什麼它可能還不夠)、一套完整的 Python 抓取流程與分層防封鎖策略,還有一個只要兩步、完全不必寫程式的替代方案 Thunderbit,適合只想拿到資料、不想陷入除錯地獄的讀者。

試用 Thunderbit 抓取 Yelp

為什麼要用 Python 抓取 Yelp?哪些人真的會受益?

在寫任何一行程式碼之前,先問一個問題:Yelp 資料到底能帶來什麼商業價值?這個平台不只是餐廳評論網站,它其實是一個即時的在地商家資料庫,包含結構化聯絡資訊、評分、分類、營業時間,以及數億則顧客評論。

yelp_stats_bd6a43108e.png

以下是最常受益的族群,以及他們通常會抓取哪些內容:

使用情境關鍵資料欄位重要原因
業務開發與名單蒐集商家名稱、電話、網站、地址、分類、評分建立鎖定在地中小企業的潛在客戶名單 — 5 位 Yelp 使用者中有 4 位 到站時已接近購買決策
競爭情報評論、星等、評論數、情緒傾向監控競爭對手口碑、找出服務缺口、追蹤趨勢
市場研究與 NLP完整評論文字、日期、評論者中繼資料情緒分析、主題建模 — Yelp 評論是學術研究中 最常用的 NLP 語料之一
房地產與選址商家密度、分類組合、地區評論品質連鎖與零售據點選址 — Yelp 也把這類資料做成 Location Intelligence 企業產品
電商與營運價格訊號、顧客抱怨、服務時間觀察競爭對手如何被評價,找出營運模式

核心其實很一致:真正的目標是結構化資料,而 Python 只是其中一種取得方式。有些讀者想保有完整程式控制權;也有人只需要一份奧斯汀水電工聯絡名單的試算表。這篇文章兩條路都會涵蓋。

Yelp Fusion API 與 Python 網頁抓取:到底該選哪個?

很多教學都直接跳過這個決策,沒有先判斷官方的 Yelp Fusion API(現在改名為「Yelp Places API」)是否已經足夠。依我的經驗,先做這個評估可以省下大量時間,因為 API 很適合某些需求,但對另一些需求則完全不夠用。

Fusion API 實際能拿到什麼

Fusion API 提供結構化的商家搜尋、商家詳情、自動完成,以及評論端點。它是授權的、文件完整,也不需要任何反機器人技巧。

但真正卡關的是評論端點。以下是 Yelp 員工在 GitHub 上確認過的內容:

「Yelp API 不會回傳完整評論文字。預設只提供 3 則、每則 160 字元的評論摘錄。」 — Yelp 員工回覆,GitHub Issue #163

這不是 bug,而是設計如此。API 硬性限制最多 3 則評論摘要(Premium 版最多 7 則),而且每則都只會截到約 160 字元。沒有評論中繼資料(有幫助/有趣/很酷的投票)、沒有評論者歷史、也沒有店家回覆。而且在 2023 年 5 月之後,新用戶的每日配額降到 300–500 次呼叫/日,從原本的 5,000 次大幅下修。入門方案價格從 每月 29 美元 起。

判斷框架

比較項目Yelp Fusion APIPython 網頁抓取Thunderbit(免寫程式)
完整評論❌ 只有 3 則摘要(每則約 160 字)✅ 透過 GraphQL 取得所有評論✅ 取得所有可見評論
速率限制300–500/日(新帳號);5,000(舊帳號)自行管理(靠代理成本)以點數計費
設定成本約 15 分鐘(API key + SDK)幾小時到幾天約 2 分鐘
商家欄位約 20 個結構化欄位不限(可解析 HTML/JSON)AI 建議欄位
防封鎖處理不適用(授權 API)必須自己做自動處理
法規風險✅ 授權⚠️ ToS 灰色地帶⚠️ 與抓取相同
成本最低每月 29 美元免費(+ 代理費用 $0.75–$4/GB)有免費方案
維護成本低(API 穩定)高(選擇器會壞、反機器人升級)低(AI 會重新適應)

**適合使用 Fusion API 的情況:**你只需要基本商家資訊、小規模查詢,或需要授權整合,而且每家商店只要 3 則評論摘要就夠。

**適合使用 Python 抓取的情況:**你需要完整評論文字、某商家的全部評論、評論中繼資料、單次搜尋超過 240 筆結果,或預算低於每月 29 美元。

**適合使用 Thunderbit 的情況:**你想快速拿到資料,不想寫或維護程式。下面的免程式段落會更詳細說明。

免寫程式的捷徑:用 Thunderbit 抓取 Yelp(不需要 Python)

在深入 Python 之前,先給真正目標是「資料」而不是「練程式」的讀者一條最快路徑。多數競品文章都預設你會 Python,但在 Thunderbit 的實務經驗裡,我發現搜尋「scrape Yelp」的人有很大一部分其實是業務、營運主管和小型企業主,他們只想要一份在地商家試算表,不想先上 TLS 指紋辨識的速成課。

Thunderbit 已經內建 Yelp 範本:

實際使用方式

  1. 在 Chrome 中打開 Yelp 搜尋結果頁或商家頁
  2. Thunderbit 擴充功能 中點選 AI Suggest Fields,AI 會讀取頁面並建議欄位(商家名稱、評分、評論數、價格區間、分類、地址、電話、網址)
  3. 點選 Scrape — 完成

如果使用預建的 Yelp 範本,流程更簡單:打開範本,點 Scrape 就行。

子頁面抓取 會自動完成資料補全:從 Yelp 搜尋結果頁開始,開啟子頁面抓取後,Thunderbit 會逐一拜訪每個商家頁,抓取營業時間、完整評論、網站、照片與設施資訊,不需要額外設定。

分頁也會自動處理 — 不管是點擊式分頁還是捲動式載入,都已內建支援。(想了解原理,可參考我們的 分頁指南。)

所有方案都可免費匯出 — Excel、Google Sheets、Airtable、Notion、CSV、JSON,全都支援。沒有 pandas,沒有寫 CSV 的程式碼。

時間比較

時間Python 爬蟲Thunderbit
第一次執行幾小時到幾天(寫選擇器、處理分頁、代理、重試邏輯)使用內建 Yelp 範本約 30 秒
Yelp 調整標記時手動重寫選擇器再點一次 AI Suggest Fields,自動重新適應
IP 被封時除錯、輪換代理池、重新測試雲端模式自動處理 IP 輪替
匯出到 Google Sheets自己寫 OAuth + pandas 串接一鍵完成,免費

如果你先試 Thunderbit,發現它已經滿足需求,那就可以直接跳過本文剩下內容。如果你需要完整程式控制、客製欄位,或每月規模超過幾千筆資料,再繼續往下看。

抓取 Yelp 可用哪些 Python 套件?該怎麼選

「我該用 Scrapy、BS4+requests,還是 Selenium?」這是 r/webscraping 裡最常看到的 Yelp 問題之一。但每篇教學都只介紹自己喜歡的函式庫,卻沒說為什麼。下面直接講結論。

2025 年的現實:requests + BeautifulSoup 在 Yelp 上已經壞掉了

幾乎所有 Yelp 教學都在推薦的那套:pip install requests beautifulsoup4,在 2025 年會 第一個請求就被封鎖。不是第 50 個,是第一個。

原因是:Python 的 requests 套件帶出的 TLS/JA3 指紋,和任何真實瀏覽器都對不上。Yelp 的反機器人層會在 TLS 握手階段就把它標記掉,甚至在讀到你的 User-Agent 之前就擋下來。我做了多次測試——新 IP、真實 headers、隨機延遲——但只要是原生 requests,還是立刻收到 403 Forbidden。

套件選擇矩陣

函式庫最適合支援 JS?可抗機器人?學習曲線速度
requests + BeautifulSoup簡單單頁抓取(對 Yelp 已失效)非常低快(直到被封)
httpx async + parsel大規模非同步抓取非常快
curl_cffi + parselYelp 專用:TLS 模擬✅ TLS/JA3/HTTP2非常快
Scrapy 2.14含分頁的完整爬取流程部分(透過 scrapy-playwright)AutoThrottle、重試 middleware中高
Selenium 4.43 / Playwright 1.58JS 很重的頁面、CAPTCHA 變通方案部分中等慢(約 10–30 頁/分鐘)
Thunderbit不會寫程式、快速擷取✅(瀏覽器)內建(雲端模式)非常低

curl_cffi 的關鍵突破

真正改變我 Yelp 抓取流程的函式庫是 curl_cffi——它是 curl-impersonate 的 Python 封裝。它輸出的 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」的差別。

我對 2025 年 Yelp 抓取的推薦技術棧: curl_cffi + parsel + jmespath + residential proxies。如果你需要完整抓取流程與排程功能,可以再包一層 Scrapy 2.14,並使用基於 curl_cffi 的 downloader middleware。

建立 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 — 用 Chrome 的 TLS 指紋發送 HTTP 請求(反機器人繞過關鍵)
  • parsel — 解析 HTML 的 CSS/XPath 選擇器(與 Scrapy 同樣引擎,但更輕量)
  • jmespath — 宣告式 JSON 查詢(處理 Yelp 內嵌 JSON 比巢狀 dict 存取更乾淨)
  • pandas — 將資料匯出為 CSV / Excel

選配但很實用:

pip install fake-useragent  # 注意:這個 repo 在 2026 年 4 月已封存,但仍可安裝

逐步教學:如何用 Python 抓取 Yelp

這是核心教學。讓整個流程更穩定的關鍵洞見是:不要依賴 CSS 選擇器,改抓隱藏 JSON。 Yelp 會在建置時隨機化 CSS 類名(這週是 y-css-14xwok2,下週可能變 y-css-hcq7b9),所以任何綁死這些類名的爬蟲都會在幾週內失效。相較之下,內嵌 JSON payload —— application/ld+json schema 與 react-root-props —— 是穩定的。

步驟 2:抓取 Yelp 搜尋結果

Yelp 搜尋網址格式很固定:https://www.yelp.com/search?find_desc={term}&find_loc={location}。搜尋結果資料會以 JSON 形式嵌在 <script data-id="react-root-props"> 標籤裡,而不是藏在一堆 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

你應該會拿到一串 dict,內含商家名稱、網址、評分與評論數。如果回應裡缺少 react-root-props,代表你拿到的是被封鎖的空殼頁面——請更換 IP 再試一次。

Cookie: intl_splash=false 是 Yelp 國家地區跳轉頁的標準解法。沒加的話,非美國 IP 常會先看到一個看起來像軟封鎖、但其實不是的 splash 頁。

步驟 3:抓取 Yelp 商家頁

搜尋結果中的每個商家網址,都會導向一個資料更完整的細節頁。最穩定的擷取目標是 <script type="application/ld+json"> 區塊——裡面包含 Yelp 為 SEO 保留的 schema.org 結構化資料,而且不會刻意混淆。

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/batch,而且 GetBusinessReviewFeed 操作有一個固定的 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

每頁會回傳 10 則評論。只要把 base64 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 而不被擋下來

這一段就是本文存在的核心原因。自 2024 年底以來,Yelp 的反機器人措施明顯升級——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",
}

最容易被標記的三個錯誤:

  1. UA 說是 Chrome,但 sec-ch-ua 缺失或版本與 UA 不一致
  2. sec-ch-ua-platform 顯示「Windows」,但 UA 字串卻是 macOS
  3. 同一個 IP 對數千次請求都用完全相同的 UA——請輪換 10–20 組近期的 Chrome / Firefox / Safari 字串

第 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 次嘗試
每個 IP 的同時工作數1(每個 IP 序列化;並行請用代理)
單個 residential IP 的長期速率上限約 1 次 / 5 秒(約 12 rpm)

第 3 層:User-Agent 與 Session 輪換

使用一組真實瀏覽器的 User-Agent 池來輪替。保留 session 與 cookie,模擬真實瀏覽行為——Yelp 會用 cookie 做偵測,每次請求都開新 session 反而可疑。

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。資料中心 IP 和免費代理在 Yelp 上幾乎沒用——Yelp 的 IP 信譽層會預先封鎖 AWS、GCP 和 DigitalOcean 的 IP 範圍。

供應商入門 $/GB備註
IPRoyal$1.75/GB最便宜;也是最常被引用的 Yelp 教學來源
Decodo(前 Smartproxy)$3.20–$3.50量大時 GB/美元比最佳
Bright Data$4.00(按量付費)1.5 億+ IP 池;有專門的 Yelp Proxies 頁面
Oxylabs$6.00–$8.00高階方案;1,000 萬+ IP
Aluvia(行動 SIM)$3.00真實美國電信商行動 IP,特別為 Yelp 抓取定位

輪換式 residential proxy(每次請求換 IP)最適合大量搜尋結果爬取。Sticky session(同一個 IP 維持 10 分鐘)則更適合在商家頁 → 評論 → 分頁流程中保留 cookie。

第 5 層:偵測與處理封鎖

不是每種封鎖都長一樣。Yelp 常常給你一個通用的「頁面不可用」外殼,而不是 CAPTCHA,這也是為什麼天真的爬蟲會以為自己拿到資料,實際上卻只收到空白回應。

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 / header / TLS 已被判定失效
HTTP 429速率限制 — 通常可透過退避恢復
HTTP 503通用封鎖或服務降載
重新導向到 /error 或出現 "page not available" 內容軟封鎖
幾乎空白,只有 正在等待 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 — 具有穩定 documentId 的 GraphQL 評論端點

如果你還在解析 CSS 類名,其實等於把基礎蓋在沙上。改解析 JSON 才是正解。

第 7 層:隱身瀏覽器備援方案

只有在 curl_cffi + residential proxies 真的過不去時,才升級到 headless browser——通常是 Yelp 送出 JavaScript challenge page 或 CAPTCHA 的時候。

對 95% 的商家/搜尋/評論抓取來說,curl_cffi + 隱藏 JSON + residential proxies 比瀏覽器更快、更便宜、也更穩定。但如果你真的需要瀏覽器:

工具2025 狀態備註
rebrowser-playwright建議起手式直接可用的 Playwright 修正版,修補 CDP 洩漏
nodriverChrome 隱身最佳之一undetected-chromedriver 的後繼者;完全避開 WebDriver 協定
patchright持續維護的 Playwright 分支可通過現代偵測測試
playwright-stealth成熟穩定會修補 navigator.webdriver,並從 UA 中移除 HeadlessChrome

Yelp 上別用原生 Selenium,太容易被指紋辨識了。

Yelp Fusion API vs. Python 抓取 vs. Thunderbit:完整比較

面向Yelp Fusion APIPython 抓取Thunderbit
完整評論文字❌ 3 則摘要 × 約 160 字✅ 無上限(GraphQL)✅ 內建評論範本
評論中繼資料(投票、店家回覆)✅ 透過 AI 建議欄位
照片❌(Base 方案為 0)✅ 無上限
每次搜尋最多結果240(2024 前為 1,000)無上限(可分頁)無上限
每日速率限制300–500(新帳號)/ 5,000(舊帳號)只受代理預算限制以點數計費(Pro 版每月 3,000)
設定成本約 15 分鐘幾小時到幾天約 2 分鐘
防封鎖處理不適用你的問題已處理(雲端模式)
法律風險低(授權)中(ToS 灰色地帶)中(與抓取相同)
成本(入門)每月 29 美元約 $0.75–$4/GB 代理費 + 開發時間免費方案
成本(大量使用)每月 $643+每月 $50–$500 代理費 + 開發時間每月 $38–$49
資料匯出JSONCSV/JSON(自己寫)Excel / Sheets / Airtable / Notion — 免費
維護成本高(選擇器會壞、反機器人升級)低(AI 會重新適應)

抓取 Yelp 的法律與倫理提醒

我不是律師,以下內容也不是法律建議。不過,過去兩年法律環境的變化夠大了,在投入 Yelp 抓取專案前,至少要先知道基本規則。

Yelp 的服務條款怎麼說: 2025 年 10 月版 ToS 更新 明確禁止使用「任何機器人、蜘蛛程式……或其他自動化設備」去「存取、擷取、複製、抓取或索引」服務的任何部分。條款還新增了關於「AI Technologies 和/或其他自動化工具」的描述。

Yelp Support 也再次強調:「Yelp 不允許任何網站抓取行為。」

robots.txt 的內容: Yelp 的 robots.txtUser-agent: * 一律 Disallow: /,並且特別封鎖 GPTBot、ClaudeBot、PerplexityBot、CCBot 和 Meta-ExternalAgent。只有 Googlebot、Bingbot 與少數社群媒體爬蟲被白名單放行。

真正重要的法律先例:Meta v. Bright Data(加州北區法院,2024 年 1 月)中,法院認定抓取公開可見、未登入的資料,並不違反 Meta 的服務條款。關鍵差異在於:未登入的公開資料 vs. 登入後資料hiQ v. LinkedIn 也建立了抓取公開資料大致不會違反 CFAA 的方向,但 hiQ 最後仍在州法侵權主張(動產侵入、挪用)上敗訴,還被判賠 50 萬美元。

實務建議:

  • 只抓公開可見、未登入頁面
  • 控制請求速率(本指南中的延遲設計,同時也是倫理上的限速)
  • 不要轉賣附帶使用者姓名的原始評論文字——請尊重評論者隱私
  • 遵守當地資料保護法(CCPA、GDPR)
  • 不要登入後再抓取——這會跨過授權界線
  • 把商家資訊(名稱/地址/電話/評分)視為公開事實資料;把評論文字視為更敏感內容

實際情況請諮詢法律專業人士。

結語

三條路,同一個目標。

Yelp Fusion API 是最受授權、維護成本最低的選項,但它只提供 3 則評論摘要,而且起價是每月 29 美元。Python 抓取則能讓你完整掌握 Yelp 的每一項資料,但也需要真正的投入:curl_cffi 做 TLS 指紋模擬、residential proxies、隨機延遲、隱藏 JSON 解析,以及隨著 Yelp 防禦升級而持續維護。Thunderbit 則能讓你從「我需要 Yelp 資料」直接變成「這是我的試算表」,大約只要 30 秒,完全不用寫程式,也不用設定代理。

2025 年真正有效的防封鎖要點是:帶完整 Client Hints 的真實 headers、用 curl_cffi 模擬 TLS 指紋、隨機延遲與指數退避、residential proxy 輪換,以及最重要的——解析隱藏 JSON(application/ld+jsonreact-root-props),而不是脆弱的 CSS 選擇器。

還不確定哪條路適合你?先試試 Thunderbit 的免費方案。如果它已經滿足需求,你就省下了好幾小時;如果你需要更多控制權——完整程式化流程、客製欄位、與 CRM 的緊密整合——上面的 Python 指南就能幫上忙。若你想更深入了解抓取工具版圖,也可以看看我們整理的 最佳網頁爬蟲 Chrome 擴充功能將網站資料抓取到 Excel 的指南

試用 Thunderbit 進行 Yelp 資料擷取 Get Started Free

常見問題

可以用 Python 免費抓取 Yelp 嗎?

可以——使用 curl_cffiparseljmespath 這類免費函式庫。不過只要規模稍微大一點(超過幾十頁),你就需要付費的 residential proxies,價格大約從 IPRoyal 的 $1.75/GB 起。Thunderbit 也提供免費方案,每月可擷取 6 頁,適合快速、免寫程式的抓取需求。

Yelp 會封鎖爬蟲嗎?

會,而且非常積極。Yelp 使用 TLS/JA3 指紋辨識、IP 信譽評分、CAPTCHA、行為分析,以及輪換混淆過的 CSS 類名。原生 requests 第一個請求就會被封。本文介紹的分層防封鎖策略——curl_cffi 做 TLS 模擬、真實 headers、隨機延遲與 residential proxies——才是 2025 年有效的方法。

Yelp Fusion API 比抓取更好嗎?

要看你的需求。API 是授權且風險較低,但它只會回傳 3 則、每則約 160 字元的評論摘要,搜尋結果上限是 240 筆,而且起價每月 29 美元。如果你需要完整評論文字、評論中繼資料,或每天超過幾百筆資料,那抓取才是唯一選項。

要怎麼用 Python 抓取 Yelp 評論?

使用 curl_cffi 並設定 impersonate="chrome131" 來抓商家頁,從 <meta name="yelp-biz-id"> 取出編碼後的商家 ID,接著對 https://www.yelp.com/gql/batch 發送 GetBusinessReviewFeed 操作,並透過 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 執行長|AI 資料自動化專家 Shuai Guan 是 Thunderbit 的執行長,畢業於密西根大學工程學院。憑藉近十年在科技與 SaaS 架構領域的經驗,他專注於把複雜的 AI 模型轉化為實用、免程式碼的資料擷取工具。在這個部落格中,他分享經過實戰驗證、毫無保留的網頁爬取與自動化策略見解,幫助你打造更聰明、以數據驅動的工作流程。當他不在優化資料流程時,也會把同樣的細膩與專注投入到攝影興趣中。
目錄

只要提問,就能抓取網頁

用白話告訴它你要什麼,或者更好,什麼都不用說。

立即體驗 Thunderbit free
使用 AI 擷取資料
輕鬆將資料轉移到 Google Sheets、Airtable 或 Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week