如何抓取Yelp商家列表和评论2026:公开数据、PerimeterX与代理实战指南

面向开发者的Yelp抓取指南:解析公开商家数据与评论、绕过PerimeterX反爬虫、使用轮换住宅代理保持稳定性,包含Python和Node.js可运行示例及伦理合规建议。

How to Scrape Yelp Business Listings and Reviews in 2026: Public Data, PerimeterX, and Proxies
本文目录

免责声明:本文仅讨论对公开可访问数据的合规采集。在抓取Yelp之前,请仔细阅读其服务条款。在美国,违反计算机访问限制可能涉及《计算机欺诈和滥用法》(CFAA);在欧盟,处理包含个人信息的评论数据须遵守《通用数据保护条例》(GDPR)。如果Yelp官方API能满足你的需求,应优先使用API而非网页抓取。

如果你正在构建本地商家数据集——用于市场分析、竞品监控或评论情感分析——抓取Yelp几乎是绕不开的话题。Yelp拥有超过5300万条商家信息,是全球最大的本地商业评论平台之一。但Yelp的反爬虫体系相当成熟:PerimeterX(现为HUMAN)的行为检测、TLS指纹分析、以及IP信誉评分,让简单的requests脚本在发出去几个请求后就被封禁。本文将从技术原理到可运行代码,完整讲解如何安全、稳定地采集Yelp公开商家数据。

如何抓取Yelp商家列表和评论2026:公开数据范围与Fusion API限制

首先需要明确:哪些数据是公开可访问的,哪些需要登录。Yelp的商家页面(/biz/<slug>)在未登录状态下可以访问以下信息:

  • 商家基本信息:名称、地址、电话、营业时间、价格区间
  • 分类与标签:如“意大利餐厅”、“咖啡店”等类别
  • 评分与评论数:星级评分(1-5星)和总评论数
  • 评论文本:每条评论的用户名、评分、日期、正文内容
  • 照片元数据:用户上传的图片缩略图URL

Yelp提供了官方的Fusion API,但它的限制很大:商家搜索端点返回的评论最多只有3条,且不包含完整评论正文。对于需要批量采集评论数据的研究者或数据工程师来说,Fusion API的覆盖范围远远不够。这就是为什么开发者转向网页抓取——但这条路并不平坦。

Yelp的反爬虫防御体系

Yelp的反爬虫由多层机制组成,理解每一层是构建稳定抓取管道的前提。

PerimeterX / HUMAN 行为检测

Yelp使用PerimeterX(2024年更名为HUMAN)作为其主要的机器人检测引擎。PerimeterX的核心机制包括:

  • _px3 Cookie:这是PerimeterX生成的加密Cookie,包含设备指纹和行为评分。没有有效的_px3,请求会被直接拦截或重定向到验证页面。
  • Sensor数据挑战:浏览器端JavaScript会收集鼠标移动、键盘输入、触摸事件等行为数据,生成一个“sensor data” payload发送给PerimeterX服务器。纯HTTP客户端(如requests)无法生成这些数据。
  • IP信誉评分:PerimeterX维护IP黑名单,数据中心IP段几乎全部被标记为高风险。来自数据中心IP的请求通常在3-5个请求后触发CAPTCHA或直接403。

TLS指纹(JA3/JA4)

即使你设置了完美的User-Agent和HTTP头,TLS握手层的指纹仍然会暴露你的真实身份。Python的requests库使用urllib3底层,其TLS ClientHello与真实Chrome浏览器存在显著差异。Yelp的服务端可以检测JA3指纹,将不匹配的请求标记为自动化工具。

CAPTCHA触发条件

在以下情况下,Yelp会触发CAPTCHA挑战:

  • 短时间内从同一IP发送大量请求(通常超过10-15请求/分钟
  • 使用数据中心IP段(AWS、GCP、Azure等云服务商IP)
  • 缺少关键浏览器Cookie或JavaScript执行痕迹
  • 访问模式异常——如直接跳转到深层页面而非从搜索结果进入

为什么轮换住宅代理是必需的

面对上述防御体系,数据中心代理几乎无法工作。住宅代理(Residential Proxy)使用真实ISP分配的IP地址,在PerimeterX的IP信誉系统中具有高信任度。以下是选择代理时的关键考量:

地理定位与本地化结果

Yelp的搜索结果和商家列表具有强烈的地理偏向性。如果你用德国IP搜索“奥斯汀咖啡店”,返回的结果可能不完整或不准确。使用带有地理定位的住宅代理可以确保你获得与本地用户一致的结果:

# 美国奥斯汀的住宅代理
http://user-country-US-city-austin:pass@gate.proxyhat.com:8080

# 美国纽约的住宅代理
http://user-country-US-city-newyork:pass@gate.proxyhat.com:8080

IP轮换策略

对于Yelp抓取,推荐两种轮换策略:

  • 每请求轮换:每个HTTP请求使用不同的出口IP。适合批量抓取不同商家页面,降低单IP请求频率。
  • 粘性会话(Sticky Session):在同一会话内保持相同IP,适合需要分页抓取同一商家的所有评论时维持Cookie连续性。

Python实战:通过ProxyHat抓取Yelp评论数据

以下示例展示如何通过ProxyHat住宅代理抓取Yelp商家页面的评论数据。Yelp商家页面在HTML中嵌入了__INITIAL_STATE__ JSON blob,其中包含评论的初始数据。此外,评论分页通过review_feed JSON端点异步加载。

示例1:抓取商家页面并解析嵌入的JSON

import requests
import json
import re
import time
import random

# ProxyHat住宅代理配置 - 美国奥斯汀
proxy_url = "http://user-country-US-city-austin:YOUR_PASSWORD@gate.proxyhat.com:8080"

proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

# 轮换User-Agent列表
user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
]

def fetch_yelp_page(biz_slug):
    url = f"https://www.yelp.com/biz/{biz_slug}"
    headers = {
        "User-Agent": random.choice(user_agents),
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept-Encoding": "gzip, deflate, br",
        "Connection": "keep-alive",
        "Upgrade-Insecure-Requests": "1",
    }
    
    try:
        resp = requests.get(url, headers=headers, proxies=proxies, timeout=30)
        resp.raise_for_status()
        
        # 解析嵌入的 __INITIAL_STATE__
        pattern = r'<script[^>]*>window\.__INITIAL_STATE__\s*=\s*({.*?});?</script>'
        match = re.search(pattern, resp.text, re.DOTALL)
        
        if match:
            state_json = match.group(1)
            state = json.loads(state_json)
            
            # 提取评论数据(结构可能随Yelp更新而变化)
            reviews = state.get("biz", {}).get("reviews", [])
            
            for review in reviews[:3]:  # 仅展示前3条
                print(f"用户: {review.get('user', {}).get('name', 'N/A')}")
                print(f"评分: {review.get('rating', 'N/A')}")
                print(f"日期: {review.get('datePublished', 'N/A')}")
                print(f"评论: {review.get('comment', {}).get('text', 'N/A')[:200]}...")
                print("---")
            
            return state
        else:
            print("未找到 __INITIAL_STATE__")
            return None
            
    except requests.exceptions.RequestException as e:
        print(f"请求失败: {e}")
        return None

# 示例:抓取一个奥斯汀餐厅
fetch_yelp_page("torchys-tacos-austin")

# 请求间隔 - 保持低于10请求/分钟
time.sleep(random.uniform(8, 15))

示例2:通过review_feed端点分页抓取评论

import requests
import json
import time
import random

# 使用粘性会话 - 同一商家的分页请求保持同一IP
session_id = "yelp-torchys-001"
proxy_url = f"http://user-country-US-session-{session_id}:YOUR_PASSWORD@gate.proxyhat.com:8080"

proxies = {"http": proxy_url, "https": proxy_url}

def fetch_reviews_feed(biz_id, start=0, limit=20):
    """通过Yelp的review_feed JSON端点获取评论"""
    url = f"https://www.yelp.com/biz/{biz_id}/review_feed"
    params = {"rl": f"en", "sort_by": "relevance_desc", "start": start}
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Accept": "application/json",
        "Referer": f"https://www.yelp.com/biz/{biz_id}",
        "X-Requested-With": "XMLHttpRequest",
    }
    
    resp = requests.get(url, params=params, headers=headers, proxies=proxies, timeout=30)
    
    if resp.status_code == 200:
        data = resp.json()
        reviews = data.get("reviews", [])
        
        for r in reviews:
            # 截断的评论对象示例
            review_obj = {
                "user": r.get("user", {}).get("markupDisplayName", ""),
                "rating": r.get("rating", 0),
                "date": r.get("localizedDate", ""),
                "text": r.get("comment", {}).get("text", "")[:300],
            }
            print(json.dumps(review_obj, ensure_ascii=False, indent=2))
        
        return reviews
    else:
        print(f"状态码: {resp.status_code}")
        return []

# 分页抓取
all_reviews = []
for page in range(0, 100, 20):  # 最多5页
    print(f"\n=== 抓取第 {page//20 + 1} 页 ===")
    reviews = fetch_reviews_feed("torchys-tacos-austin", start=page)
    if not reviews:
        break
    all_reviews.extend(reviews)
    time.sleep(random.uniform(10, 20))  # 每页间隔10-20秒

print(f"\n总共获取 {len(all_reviews)} 条评论")

Node.js SOCKS5代理示例

对于Node.js环境,可以通过SOCKS5代理进行抓取。ProxyHat的SOCKS5端点位于gate.proxyhat.com:1080

const { SocksProxyAgent } = require('socks-proxy-agent');
const axios = require('axios');

// ProxyHat SOCKS5配置 - 美国住宅代理
const socksUrl = 'socks5://user-country-US:YOUR_PASSWORD@gate.proxyhat.com:1080';
const agent = new SocksProxyAgent(socksUrl);

async function scrapeYelpBiz(slug) {
    const url = `https://www.yelp.com/biz/${slug}`;
    
    const config = {
        url,
        httpsAgent: agent,
        httpAgent: agent,
        timeout: 30000,
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml',
            'Accept-Language': 'en-US,en;q=0.9',
            'Accept-Encoding': 'gzip, deflate, br',
        },
    };
    
    try {
        const resp = await axios(config);
        
        // 提取 __INITIAL_STATE__
        const match = resp.data.match(/window\.__INITIAL_STATE__\s*=\s*({[\s\S]*?});/);
        if (match) {
            const state = JSON.parse(match[1]);
            const biz = state.biz || {};
            
            const bizData = {
                name: biz.name || '',
                rating: biz.rating || 0,
                reviewCount: biz.reviewCount || 0,
                categories: (biz.categories || []).map(c => c.title),
                hours: biz.hours || [],
            };
            
            console.log(JSON.stringify(bizData, null, 2));
            return bizData;
        }
    } catch (err) {
        console.error('抓取失败:', err.message);
    }
}

scrapeYelpBiz('torchys-tacos-austin');

分页、速率控制与会话管理最佳实践

请求速率与节奏控制

Yelp对高频请求非常敏感。推荐的速率控制策略:

  • 单IP速率:不超过8-10请求/分钟
  • 请求间隔:随机化8-20秒间隔,避免固定节奏被检测
  • 并发控制:同一代理会话串行请求,不同代理可并行但总并发不超过50-100会话
  • 退避策略:遇到429或403时,立即停止当前IP,切换新IP并等待60秒以上

粘性会话的使用场景

# 商家A的评论分页 - 使用粘性会话保持IP一致
http://user-country-US-session-bizA-page1:pass@gate.proxyhat.com:8080

# 商家B的评论分页 - 使用不同会话
http://user-country-US-session-bizB-page1:pass@gate.proxyhat.com:8080

粘性会话确保同一商家的分页请求来自同一IP,这更接近真实用户浏览行为。切换到新商家时更换会话ID,实现自然的IP轮换。

User-Agent轮换

不要只使用单一User-Agent。准备一个包含10-20个当前主流浏览器UA的列表,每次请求随机选择。但注意:同一粘性会话内应保持相同UA,否则会触发异常检测。

代理类型对比

代理类型Yelp抓取适用性IP信誉成本推荐场景
数据中心代理极差 - 3-5请求即被封最低(约$0.5/GB)不推荐
住宅代理优秀 - 高信任度IP中等(约$3-5/GB)Yelp抓取首选
移动代理优秀 - 最高信任度最高最高(约$10+/GB)高价值目标页面

对于Yelp这类受PerimeterX保护的目标,住宅代理是性价比最优的选择。查看ProxyHat的定价页面了解住宅代理套餐详情,或访问代理位置页面确认覆盖的地理位置。

常见错误与边界情况

1. 忽略robots.txt

Yelp的robots.txt明确禁止部分路径的爬取。在开始抓取前,务必检查robots.txt并遵守其规则。虽然robots.txt本身不具备法律强制力,但违反它可能被用作CFAA诉讼的证据。

2. 评论中的个人数据

Yelp评论包含用户姓名、头像和位置信息。在GDPR下,这些属于个人数据。如果你计划存储或处理评论数据集:

  • 对用户名进行匿名化或假名化处理
  • 不存储可识别个人身份的头像URL
  • 明确数据保留期限,不要无限期存储
  • 如果面向欧盟用户,考虑是否需要数据处理协议

3. 结构变化导致解析失败

Yelp会不定期更新前端结构,__INITIAL_STATE__的JSON schema可能随时变化。建议:

  • 使用防御性解析(.get()配合默认值)
  • 监控解析失败率,超过15%时检查结构变化
  • 对关键数据字段做schema验证

即使使用住宅代理,_px3 Cookie也可能过期。如果请求开始返回403或重定向到验证页面,说明Cookie已失效。解决方案是使用无头浏览器(如Playwright)定期刷新Cookie,或使用ProxyHat的会话保持功能确保Cookie连续性。

伦理抓取与何时使用官方API

网页抓取是一把双刃剑。在构建Yelp数据采集管道时,请遵循以下原则:

优先使用Fusion API

如果你的需求只需要商家基本信息(名称、地址、评分、分类)和少量评论样本,Yelp Fusion API是更合规、更稳定的选择。API调用受官方授权,不会触发反爬虫机制,且数据结构稳定。Fusion API的免费层级允许5000次/天的API调用。

网页抓取的合理场景

  • 需要采集超过3条的完整评论数据用于学术研究
  • 需要分析评论的时间趋势和情感变化
  • Fusion API未覆盖的商家属性(如照片元数据)

应避免的行为

  • 不要抓取需要登录才能访问的内容(如私信、个人资料详情)
  • 不要对Yelp服务器造成显著负载——保持低速率、低并发
  • 不要将抓取的数据用于垃圾营销或恶意用途
  • 不要绕过付费墙或访问限制

更多关于合规抓取的最佳实践,请参考ProxyHat的网页抓取用例SERP追踪用例页面。如需技术文档,请访问ProxyHat官方文档

关键要点总结

Yelp抓取核心要点:

  • Yelp公开页面可获取商家信息、评分和评论,但Fusion API仅返回3条评论
  • PerimeterX/HUMAN的行为检测和IP信誉评分是主要障碍,数据中心IP几乎不可用
  • 轮换住宅代理配合US地理定位是稳定抓取的基础
  • 请求速率控制在8-10请求/分钟,使用随机间隔和粘性会话
  • 解析__INITIAL_STATE__review_feed端点获取结构化数据
  • 遵守TOS、CFAA和GDPR,优先使用Fusion API覆盖基础需求
  • 对评论中的个人数据进行匿名化处理

准备好开始抓取了吗?前往ProxyHat定价页面选择适合你的住宅代理套餐,或注册后访问控制面板开始配置你的第一个代理会话。

常见问题

抓取Yelp商家列表和评论是什么意思?

抓取Yelp商家列表和评论是指通过自动化程序采集Yelp网站上公开可见的商家信息(名称、地址、评分、分类、营业时间)和用户评论数据。与官方Fusion API不同,网页抓取可以获取超过3条的完整评论正文,但需要应对PerimeterX反爬虫机制和IP封禁问题。

为什么抓取Yelp需要使用代理?

Yelp使用PerimeterX/HUMAN反爬虫系统,对数据中心IP进行信誉评分。数据中心IP通常在3-5个请求后就会被封禁或触发CAPTCHA。住宅代理使用真实ISP分配的IP地址,具有高信任度,能够有效绕过IP信誉检测。此外,带有US地理定位的住宅代理还能确保获取与本地用户一致的搜索结果。

哪种代理类型最适合抓取Yelp?

住宅代理是抓取Yelp的最佳选择。它们使用真实ISP的IP地址,在PerimeterX的IP信誉系统中具有高信任度,成功率远高于数据中心代理。移动代理信任度更高但成本也更高。对于Yelp这类受高级反爬虫保护的目标,住宅代理在成本和可靠性之间提供了最佳平衡,建议配合US地理定位和粘性会话使用。

如何避免抓取Yelp时被封禁?

避免封禁的关键策略包括:使用轮换住宅代理而非数据中心IP;控制请求速率在8-10请求/分钟以下;使用随机化请求间隔(8-20秒);轮换User-Agent(10-20个主流浏览器UA);使用粘性会话保持同一商家的分页请求来自同一IP;遇到403或429时立即切换IP并等待60秒以上;遵守Yelp的robots.txt和服务条款。

Yelp Fusion API和网页抓取有什么区别?

Yelp Fusion API是官方提供的REST API,免费层级允许每天5000次调用,但每个商家最多只返回3条评论且不含完整正文。网页抓取可以获取完整评论数据、照片元数据等API未覆盖的信息,但需要应对反爬虫机制且需遵守TOS。建议优先使用Fusion API满足基础需求,仅在需要完整评论数据集时才考虑网页抓取。

抓取时你的代理被封了吗?

免费检测代理 — 几秒内查看封锁率、速度和匿名性。无需注册。

免费检测我的代理
← 返回博客