주의: 이 글은 각 플랫폼의 이용약관(Terms of Service)과 관련 법률을 준수하는 합법적인 공개 데이터 접근만을 다룹니다. 미국의 CFAA(Computer Fraud and Abuse Act)와 유럽연합의 GDPR(General Data Protection Regulation)을 반드시 숙지하세요. 로그인이 필요한 콘텐츠는 스크래핑하지 말고, 개인정보가 포함된 리뷰 데이터 수집 시에는 익명화 및 최소화 원칙을 따르세요.
2026년 Yelp 비즈니스 리스팅 및 리뷰 스크래핑은 로컬 비즈니스 데이터셋을 구축하려는 개발자에게 여전히 핵심 과제입니다. Yelp는 전 세계적으로 수백만 개의 비즈니스 프로필과 리뷰를 보유하고 있지만, PerimeterX/HUMAN 기반의 강력한 안티봇 시스템으로 데이터 접근을 제어합니다. 이 가이드에서는 공개적으로 접근 가능한 데이터의 범위, 방어 메커니즘의 작동 방식, residential proxy를 활용한 안정적인 수집 전략, 그리고 윤리적 스크래핑 원칙까지 단계적으로 설명합니다.
2026년 Yelp 비즈니스 리스팅 및 리뷰 스크래핑: 개요
Yelp 스크래핑은 /biz/<slug> 경로의 비즈니스 프로필 페이지에서 공개 정보를 수집하는 작업입니다. 별점, 카테고리, 영업시간, 리뷰 텍스트 등은 로그인 없이 누구나 볼 수 있는 공개 데이터입니다. 하지만 Yelp의 공식 Fusion API는 비즈니스 상세 정보 엔드포인트에서 최대 3개의 리뷰만 반환하므로, 대규모 리뷰 데이터셋이 필요한 경우 웹 스크래핑이 불가피합니다.
Yelp 리뷰 스크래퍼를 구축할 때 직면하는 주요 장애물은 세 가지입니다:
- PerimeterX/HUMAN 안티봇:
_px3쿠키와 센서 챌린지로 봇 트래픽을 탐지합니다. - TLS 핑거프린팅: 비브라우저 클라이언트의 JA3 핑거프린트를 감지합니다.
- IP 평판 점수: 데이터센터 IP 대역에서 들어오는 요청은 수 회 내에 CAPTCHA로 차단됩니다.
이 가이드는 이러한 장애물을 우회하면서 합법적인 공개 데이터를 수집하는 실용적인 방법을 제공합니다.
공개적으로 접근 가능한 Yelp 데이터
로그인 없이 접근 가능한 데이터
Yelp의 비즈니스 프로필 페이지(https://www.yelp.com/biz/<slug>)는 로그인 없이 다음 정보를 공개합니다:
- 비즈니스명, 주소, 전화번호, 웹사이트 URL
- 별점(1~5점)과 리뷰 총 개수
- 카테고리 태그(예: Italian, Pizza, Coffee & Tea)
- 영업시간 및 휴무 정보
- 리뷰 텍스트, 작성자 표시명, 작성일, 평점
- 사진 메타데이터(썸네일 URL, 캡션)
Fusion API의 한계
Yelp Fusion API의 GET /businesses/{id} 엔드포인트는 비즈니스 기본 정보와 함께 최대 3개의 리뷰만 반환합니다. 이는 API 키를 발급받은 정식 사용자에게도 적용되는 제한입니다. 수백 개의 리뷰가 있는 인기 비즈니스의 경우, API만으로는 전체 리뷰 데이터셋을 구축할 수 없습니다.
반면, 웹 페이지 자체에는 페이지네이션을 통해 최대 수십 개의 리뷰가 임베드되어 있습니다. Yelp는 페이지 로딩 시 window.__INITIAL_STATE__ 객체에 비즈니스 정보와 리뷰를 JSON 형태로 포함시키며, 추가 리뷰는 /biz/<slug>/review_feed 엔드포인트를 통해 AJAX로 로드됩니다.
Yelp의 안티봇 방어 체계
PerimeterX/HUMAN과 _px3 쿠키
Yelp는 HUMAN Security(구 PerimeterX)의 봇 방어 솔루션을 사용합니다. 이 시스템의 핵심은 _px3 쿠키입니다. 이 쿠키는 브라우저 환경에서 수집된 센서 데이터(마우스 움직임, 키보드 타이밍, 캔버스 핑거프린트, WebGL 렌더러 정보 등)를 기반으로 생성됩니다.
일반적인 HTTP 클라이언트(requests, axios 등)는 이 센서 데이터를 생성할 수 없으므로, 유효한 _px3 쿠키를 획득하지 못합니다. PerimeterX는 이를 감지하면 즉시 403 응답이나 CAPTCHA 챌린지 페이지를 반환합니다.
TLS 핑거프린팅
PerimeterX는 TLS 핸드셰이크 단계에서 클라이언트의 JA3 핑거프린트를 수집합니다. Python requests 라이브러리가 생성하는 TLS 핑거프린트는 Chrome이나 Firefox의 것과 확연히 다르므로, User-Agent 헤더를 브라우저로 위장해도 TLS 레벨에서 탐지됩니다. 이를 우회하려면 curl_cffi나 tls-client 같은 라이브러리를 사용하여 브라우저와 동일한 TLS 핑거프린트를 생성해야 합니다.
CAPTCHA와 데이터센터 IP 차단
데이터센터 IP(AWS, GCP, Azure 등의 IP 대역)에서 들어오는 요청은 PerimeterX의 IP 평판 데이터베이스에서 즉시 플래그됩니다. 실제 테스트에서 데이터센터 IP를 사용한 경우, 평균 3~5회 요청 내에 CAPTCHA 챌린지가 트리거됩니다. 반면, residential IP를 사용하면 동일한 요청 패턴으로도 수십 회 이상 정상 응답을 받을 수 있습니다.
Residential 프록시가 필요한 이유
Yelp 스크래핑에 residential proxy가 필수적인 이유는 IP 평판과 지역 타겟팅 두 가지 측면에서 찾을 수 있습니다.
IP 평판: PerimeterX는 IP의 ASN(Autonomous System Number)을 확인하여 데이터센터 IP를 식별합니다. Residential IP는 실제 ISP(예: Comcast, AT&T)에서 할당된 IP이므로 일반 사용자 트래픽으로 인식됩니다. 100개의 동시 세션을 유지하면서도 차단률을 5% 이하로 유지하려면 residential proxy가 사실상 유일한 선택입니다.
지역 타겟팅: Yelp의 검색 결과와 비즈니스 리스팅은 사용자의 지역에 따라 달라집니다. 예를 들어, 오스틴의 레스토랑 리뷰를 수집하려면 오스틴 지역 IP에서 접근해야 현지 사용자가 보는 것과 동일한 결과를 얻을 수 있습니다. ProxyHat의 -country-US-city-austin 플래그를 사용하면 이러한 지역 타겟팅이 가능합니다.
| 프록시 유형 | IP 평판 | Yelp 차단 확률 | 지역 타겟팅 | 추천 용도 |
|---|---|---|---|---|
| Datacenter | 낮음 (ASN 기반 식별) | 매우 높음 (3~5회 내 차단) | 불가능 | 비추천 |
| Residential | 높음 (실제 ISP IP) | 낮음 (적절한 회전 시 <5%) | 국가/도시 단위 | Yelp 스크래핑 최적 |
| Mobile | 최고 (셀룰러 IP) | 매우 낮음 | 국가 단위 | 초고가치 타겟 |
ProxyHat은 residential, mobile, datacenter 프록시를 모두 제공합니다. Yelp 스크래핑에는 residential 프록시를 권장하며, 프록시 가격 페이지에서 요금제를 확인할 수 있습니다.
Python으로 Yelp 리뷰 스크래핑하기
다음은 ProxyHat residential proxy를 통해 Yelp 비즈니스 페이지에 접근하고, 임베드된 __INITIAL_STATE__ JSON 블롭에서 리뷰 데이터를 추출하는 Python 예제입니다.
import requests
import json
import re
import time
import random
# ProxyHat residential proxy — 미국 오스틴 지역
proxy_url = "http://user-country-US-city-austin:pass@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/119.0.0.0 Safari/537.36",
]
def scrape_yelp_biz(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",
"Referer": "https://www.yelp.com/",
}
resp = requests.get(url, proxies=proxies, headers=headers, timeout=30)
if resp.status_code == 403:
print("PerimeterX 차단 감지 — IP 회전 필요")
return None
# __INITIAL_STATE__ 블롭 추출
match = re.search(
r'window\.__INITIAL_STATE__\s*=\s*({.*?});\s*</script>',
resp.text,
re.DOTALL
)
if not match:
print("__INITIAL_STATE__ 블롭을 찾을 수 없음")
return None
state = json.loads(match.group(1))
# 비즈니스 기본 정보
biz = state.get("biz", {}).get("bizDetails", {})
print(f"비즈니스명: {biz.get('name')}")
print(f"별점: {biz.get('rating')}")
print(f"리뷰 수: {biz.get('reviewCount')}")
# 리뷰 추출 (최대 3개 출력)
reviews = state.get("review", {}).get("reviews", [])
for r in reviews[:3]:
print(f" 평점: {r.get('rating')}")
print(f" 작성자: {r.get('author', {}).get('name', 'N/A')}")
text = r.get('comment', {}).get('text', '')
print(f" 리뷰: {text[:200]}...")
print(" ---")
return state
# 실행
scrape_yelp_biz("some-business-austin")
위 코드는 단일 비즈니스 페이지에서 __INITIAL_STATE__ 블롭을 추출하고, 그 안에서 비즈니스 정보와 리뷰를 파싱합니다. 실제 운영 환경에서는 curl_cffi 라이브러리를 사용하여 TLS 핑거프린트를 브라우저와 일치시키는 것을 권장합니다.
review_feed 엔드포인트로 추가 리뷰 수집
def fetch_review_feed(biz_slug, start=0, count=20):
"""review_feed JSON 엔드포인트에서 추가 리뷰를 수집합니다."""
url = f"https://www.yelp.com/biz/{biz_slug}/review_feed"
params = {"start": start, "count": count}
headers = {
"User-Agent": random.choice(user_agents),
"Accept": "application/json",
"Referer": f"https://www.yelp.com/biz/{biz_slug}",
"X-Requested-With": "XMLHttpRequest",
}
# sticky session으로 동일 IP 유지
sticky_proxy = (
"http://user-country-US-city-austin-"
f"session-yelp-{biz_slug}:pass@gate.proxyhat.com:8080"
)
sticky_proxies = {"http": sticky_proxy, "https": sticky_proxy}
resp = requests.get(
url, params=params, proxies=sticky_proxies,
headers=headers, timeout=30
)
if resp.status_code == 200:
data = resp.json()
reviews = data.get("reviews", [])
for r in reviews:
print(f" 평점: {r.get('rating')} | "
f"{r.get('comment', {}).get('text', '')[:120]}...")
return reviews
else:
print(f"review_feed 요청 실패: HTTP {resp.status_code}")
return []
# 첫 페이지 이후 리뷰 수집
fetch_review_feed("some-business-austin", start=20, count=20)
Node.js로 SOCKS5 프록시 사용하기
다음은 Node.js에서 ProxyHat의 SOCKS5 프록시(포트 1080)를 사용하여 Yelp에 접근하는 예제입니다.
const { SocksProxyAgent } = require('socks-proxy-agent');
const axios = require('axios');
// ProxyHat SOCKS5 — sticky session으로 IP 일관성 유지
const agent = new SocksProxyAgent(
'socks5://user-country-US-session-yelp-001:pass@gate.proxyhat.com:1080'
);
async function scrapeYelpReviews(bizSlug) {
const url = `https://www.yelp.com/biz/${bizSlug}`;
try {
const resp = await axios.get(url, {
httpAgent: agent,
httpsAgent: agent,
headers: {
'User-Agent':
'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',
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'en-US,en;q=0.9',
'Referer': 'https://www.yelp.com/',
},
timeout: 30000,
});
if (resp.status === 403) {
console.error('PerimeterX 차단 — 세션 교체 필요');
return null;
}
// __INITIAL_STATE__ 추출
const match = resp.data.match(
/window\.__INITIAL_STATE__\s*=\s*({.*?});\s*<\/script>/s
);
if (!match) {
console.error('__INITIAL_STATE__ 블롭 없음');
return null;
}
const state = JSON.parse(match[1]);
const biz = state?.biz?.bizDetails || {};
const reviews = state?.review?.reviews || [];
console.log(`비즈니스: ${biz.name} (별점 ${biz.rating})`);
console.log(`리뷰 수: ${biz.reviewCount}`);
reviews.slice(0, 3).forEach((r) => {
console.log(` 평점: ${r.rating}`);
console.log(
` 리뷰: ${(r.comment?.text || '').slice(0, 200)}...`
);
console.log(' ---');
});
return state;
} catch (err) {
console.error('요청 실패:', err.message);
return null;
}
}
scrapeYelpReviews('some-business-austin');
SOCKS5는 HTTP 프록시보다 오버헤드가 적고 TCP 레벨에서 투명하게 작동하므로, 대량 수집 시 성능 이점이 있습니다. 자세한 프록시 설정은 ProxyHat 문서를 참조하세요.
페이지네이션, 속도 제한, 세션 관리
페이지네이션 전략
Yelp의 review_feed 엔드포인트는 start 파라미터로 오프셋 기반 페이지네이션을 지원합니다. 일반적으로 페이지당 20개의 리뷰를 반환합니다. 500개 이상의 리뷰를 가진 비즈니스의 경우, 25회 이상의 추가 요청이 필요합니다.
def scrape_all_reviews(biz_slug, max_reviews=500):
all_reviews = []
batch_size = 20
for start in range(0, max_reviews, batch_size):
reviews = fetch_review_feed(biz_slug, start=start, count=batch_size)
if not reviews:
print(f"리뷰 수집 중단: start={start}")
break
all_reviews.extend(reviews)
print(f"수집됨: {len(all_reviews)} / {max_reviews}")
# 요청 간 2~5초 랜덤 지연
time.sleep(random.uniform(2, 5))
return all_reviews
속도 제한 및 요청 페이싱
PerimeterX는 단일 IP에서 단위 시간당 너무 많은 요청을 보내는 경우 차단을 트리거합니다. 안정적인 수집을 위해 다음 지침을 따르세요:
- 요청 간 2~5초의 랜덤 지연을 적용하세요. 일정한 간격보다 랜덤 지연이 더 자연스럽게 보입니다.
- 단일 IP당 시간당 100~150회 요청을 상한으로 설정하세요.
- 5~10개 비즈니스를 처리한 후 sticky session을 교체하여 IP를 회전하세요.
- 동시 연결 수는 IP당 1~2개로 유지하세요. 병렬 요청은 탐지 확률을 크게 높입니다.
Sticky session으로 연속성 유지
하나의 비즈니스 페이지에서 여러 리뷰 페이지를 수집할 때는 동일한 IP를 유지하는 것이 중요합니다. IP가 중간에 바뀌면 PerimeterX가 세션 하이재킹으로 간주할 수 있습니다. ProxyHat의 -session-{id} 플래그를 사용하면 특정 세션 ID에 대해 동일한 exit IP를 유지할 수 있습니다.
# 비즈니스 A용 sticky session
proxy_a = "http://user-country-US-session-biz-a:pass@gate.proxyhat.com:8080"
# 비즈니스 B용 sticky session (다른 IP)
proxy_b = "http://user-country-US-session-biz-b:pass@gate.proxyhat.com:8080"
User-Agent 회전으로 핑거프린트 위험 완화
동일한 User-Agent로 수백 개의 요청을 보내면 핑거프린트 기반 탐지에 노출됩니다. 5~10개의 최신 브라우저 User-Agent 문자열을 준비하고 요청마다 무작위로 선택하세요. 단, User-Agent와 TLS 핑거프린트가 일치해야 합니다(예: Chrome UA와 Chrome TLS 핑거프린트). curl_cffi의 impersonate="chrome120" 옵션을 사용하면 이를 자동으로 처리할 수 있습니다.
ProxyHat의 프록시 위치 페이지에서 전 세계 190개국 이상의 IP를 확인할 수 있으며, 웹 스크래핑 사용 사례 페이지에서 더 다양한 스크래핑 시나리오를 참조하세요.
윤리적 스크래핑과 공식 API 활용
robots.txt 준수
스크래핑을 시작하기 전에 반드시 Yelp의 robots.txt를 확인하세요. robots.txt는 검색 엔진 크롤러와 스크래퍼에 대한 접근 규칙을 정의합니다. Yelp의 robots.txt는 특정 경로에 대한 크롤링을 명시적으로 제한할 수 있으며, 이를 무시하는 것은 윤리적, 법적 위험이 있습니다.
개인 데이터와 GDPR
Yelp 리뷰에는 작성자의 표시명, 프로필 사진, 위치 정보 등이 포함됩니다. EU 사용자의 데이터를 수집하는 경우 GDPR이 적용될 수 있습니다. 리뷰 텍스트에서 작성자를 식별할 수 있는 정보를 익명화하고, 데이터 보존 기간을 명확히 설정하세요. 개인 식별 정보(PII)를 상업적 목적으로 무단 수집·판매하는 것은 GDPR 제5조에 위배됩니다.
로그인이 필요한 콘텐츠는 수집하지 마세요
Yelp의 일부 기능(예: 친구의 리뷰, 비공개 메시지, 로그인 후에만 보이는 추천 리뷰)은 인증이 필요합니다. 이러한 콘텐츠를 스크래핑하기 위해 자격 증명을 자동화하거나 세션을 하이재킹하는 것은 CFAA 위반이 될 수 있습니다. 공개적으로 접근 가능한 데이터만 수집하세요.
공식 Fusion API를 우선 고려하세요
Yelp Fusion API는 비즈니스 검색, 상세 정보, 리뷰(3개 제한), 자동 완성 등의 엔드포인트를 제공합니다. 필요한 데이터가 다음 조건을 만족한다면 API를 사용하는 것이 더 안전하고 안정적입니다:
- 비즈니스당 3개 이하의 리뷰로 충분한 경우
- 비즈니스 기본 정보(이름, 주소, 별점, 카테고리)만 필요한 경우
- 일일 요청 수가 API 할당량 내에 있는 경우
API 기반 접근은 이용약관을 명시적으로 준수하는 것이며, PerimeterX 차단이나 IP 밴의 위험이 없습니다. 대규모 리뷰 데이터셋이 반드시 필요한 경우에만 웹 스크래핑을 고려하고, 그 경우에도 공개 데이터에 한정하세요.
핵심 요약
Key Takeaways:
- Yelp의 공개 비즈니스 프로필(
/biz/<slug>)은 로그인 없이 별점, 카테고리, 영업시간, 리뷰 텍스트를 제공합니다.- Fusion API는 비즈니스당 최대 3개 리뷰만 반환하므로, 대규모 리뷰 수집에는 웹 스크래핑이 필요합니다.
- PerimeterX/HUMAN의
_px3쿠키와 TLS 핑거프린팅은 데이터센터 IP를 3~5회 내에 차단합니다.- Residential proxy와 US 지역 타겟팅(
-country-US-city-austin)은 현지화된 결과와 낮은 차단률을 보장합니다.- 요청 간 2~5초 랜덤 지연, sticky session, User-Agent 회전을 조합하여 탐지 확률을 최소화하세요.
- robots.txt를 준수하고, 로그인이 필요한 콘텐츠는 수집하지 않으며, 가능하면 Fusion API를 우선 사용하세요.
Yelp 비즈니스 데이터 수집을 시작할 준비가 되었다면 ProxyHat 요금제를 확인하거나 SERP 추적 사용 사례에서 추가 인사이트를 얻으세요.






