제 Amazon 리뷰 스크래퍼는 6주 내내 멀쩡히 돌았어요. 그러던 어느 아침, 응답은 분명 200 OK인데 화면엔 아무것도 없더군요. 에러도, CAPTCHA도 없었어요. 리뷰 수백 개가 있어야 할 자리에 텅 빈 HTML만 덩그러니 남아 있었죠.
이 장면이 익숙하다면 혼자만의 일이 아니에요. 2025년 말 Amazon이 리뷰 페이지 전체를 로그인 뒤로 밀어 넣으면서, 수많은 파이썬 스크립트가 하룻밤 새 죽어 버렸거든요. 저는 지난 몇 달간 Thunderbit에서 AI 스크래퍼를 만들면서 개인용 파이썬 리뷰 파이프라인도 함께 굴려 왔어요. 그래서 제 스크립트가 처음 멈췄을 때 정말 절실했던 가이드를 직접 써 보기로 했어요. 이 글은 실제로 돌아가는 방법만 담았어요. 쿠키 기반 인증, Amazon의 CSS 난독화를 버텨 내는 안정적인 셀렉터, 10페이지 한도 우회법, 봇 차단 대응, 그리고 원본 리뷰를 실질적 인사이트로 바꾸는 감성 분석까지요. 읽다가 "이걸 내가 다 유지보수해야 하나" 싶어지면, 같은 일을 Python 없이 2분이면 끝내는 Thunderbit 방식도 보여 드릴게요.
Amazon 리뷰 스크래핑이란 무엇이며, 왜 중요한가?
Amazon 리뷰 스크래핑은 상품 페이지에서 별점, 리뷰 본문, 작성자 이름, 날짜, 인증 구매 배지 같은 고객 리뷰 데이터를 프로그램으로 뽑아내는 작업이에요. Amazon이 2010년에 Product Advertising API에서 리뷰 콘텐츠를 빼 버린 뒤 다시 살리지 않았기 때문에, 이 데이터에 프로그래밍 방식으로 닿으려면 사실상 웹 스크래핑밖에 없어요.
숫자가 이를 뒷받침해요. 구매자의 95%가 구매 전 리뷰를 읽고, 94%는 제품 리뷰의 1순위 출처로 Amazon을 꼽아요. 상품 페이지에 리뷰를 단 5개만 띄워도 전환율이 270% 뛸 수 있고, 리뷰 감성을 체계적으로 분석하는 기업은 고객 유지율이 최대 15% 높아진다는 결과도 있어요. 막연한 데이터 과학 얘기가 아니에요. 경쟁 정보, 제품 개선 신호, 마케팅 문구가 Amazon 서버에 텍스트로 그대로 쌓여 있는 셈이죠.
왜 Python으로 Amazon 리뷰를 스크래핑할까?
이 작업엔 Python이 여전히 1순위예요. 2024 Stack Overflow 개발자 설문에서 가장 선호하는 언어 1위였고, requests·BeautifulSoup·pandas·Scrapy 같은 생태계 덕분에 전업 개발자가 아니어도 비교적 쉽게 웹 스크래핑에 발을 들일 수 있어요.
이 데이터를 팀마다 쓰는 방식도 제각각이에요.
| 팀 | 활용 사례 | 추출하는 정보 |
|---|---|---|
| 제품 / R&D | 반복적으로 나오는 불만 파악, 개선 우선순위 설정 | 1~2점 리뷰 텍스트, 키워드 빈도 |
| 영업 | 경쟁사 제품 반응 모니터링 | 평점, 리뷰 수 추이 |
| 마케팅 | 광고 문구에 쓸 고객 언어 확보 | 긍정적인 리뷰 표현, 기능 언급 |
| 이커머스 운영 | 자사 제품 감성 흐름 추적 | 별점 분포, 인증 구매 비율 |
| 시장 조사 | 카테고리 리더 간 기능 비교 | 여러 ASIN의 리뷰 데이터셋 |
한 주방용품 브랜드는 부정 리뷰를 분석해 "논스틱 코팅이 벗겨진다"는 언급이 22%나 된다는 걸 발견했고, 제품을 다시 설계해 60일 만에 #1 베스트셀러 자리를 되찾았어요. 어떤 피트니스 트래커 회사는 리뷰에서 "밴드가 피부를 자극한다"는 표현을 잡아내 라텍스 알레르기 문제를 찾았고, 저자극 버전을 낸 뒤 반품률을 40% 줄였어요. 이 정도 ROI면 개발 공수를 들일 만하죠.
로그인 벽: 왜 Amazon 리뷰 스크래퍼가 멈췄을까?
2024년 11월 14일, Amazon이 전체 상품 리뷰 페이지를 보려면 로그인을 요구하기 시작했어요. 이 변화는 커뮤니티 포럼과 스크래핑 업체 블로그 곳곳에서 확인됐어요. 시크릿 창에서 /product-reviews/{ASIN}/에 들어가면 리뷰 대신 로그인 페이지로 튕겨 나가요.

증상이 은근히 교묘해요. 스크립트는 200 OK를 돌려주는데, HTML 본문엔 리뷰 대신 로그인 폼(name="email", id="ap_password")만 들어 있어요. 에러 코드도, CAPTCHA도 없죠. 그냥... 쓸 만한 내용이 없을 뿐이에요.
Amazon은 봇 차단과 지역별 규정 준수를 노리고 이렇게 바꿨어요. 적용도 들쭉날쭉해요. 특히 첫 페이지에서는 새 브라우저 창이 벽을 만나기 전에 리뷰 몇 개를 잠깐 보여 주기도 해요. 그래도 대규모 스크래퍼라면 이 벽이 늘 켜져 있다고 가정하는 편이 맞아요.
Amazon의 각 국가 도메인(.de, .co.uk, .co.jp)은 이 로그인 벽을 따로따로 적용해요. 어떤 포럼 사용자 말처럼 "나라마다 따로 로그인해야 한다"는 뜻이죠. .com 쿠키가 .co.uk에서 통하지 않아요.
대표 리뷰 vs 전체 리뷰: 로그인 없이 아직 볼 수 있는 것
Amazon 상품 페이지(/dp/{ASIN}/ URL)는 인증 없이도 대표 리뷰 약 8개를 보여 줘요. Amazon 알고리즘이 골라 주는 거라 간단한 감성 확인엔 쓸모 있지만, 정렬·필터·페이지네이션은 안 돼요.
전체 리뷰 페이지(/product-reviews/{ASIN}/)는 최신순 정렬, 별점 필터, 수백 개 리뷰 페이지 넘김까지 되지만 로그인을 해야 들어갈 수 있어요.
상태만 슥 훑고 싶다면 상품 페이지를 긁으면 돼요. 수백, 수천 개가 필요하다면 인증 처리가 필요해요.
시작 전 준비사항: Python 환경과 라이브러리
코드를 짜기 전에 준비할 것들이에요.
- 난이도: 중급 (Python에 익숙하고, 기본 HTML 구조를 이해하면 충분)
- 소요 시간: 전체 파이프라인 약 45분, 기본 스크래핑은 약 10분
- 필요한 것: Python 3.8+, Chrome 브라우저, 유효한 Amazon 계정
핵심 라이브러리를 설치해요.
pip install requests beautifulsoup4 lxml pandas textblob
선택 사항(고급 감성 분석용):
pip install transformers torch
ASIN이 뭐냐고요? Amazon의 10자리 상품 식별자예요. 상품 URL 어디서든 찾을 수 있어요. 예를 들어 amazon.com/dp/B0BCNKKZ91이면 ASIN은 B0BCNKKZ91예요. 이 값을 리뷰 URL에 넣게 돼요.
1단계: 쿠키 기반 인증으로 로그인 벽 넘기기
가장 안정적인 방법은 브라우저에서 Amazon에 로그인한 뒤 세션 쿠키를 복사해 Python requests.Session()에 넣는 거예요. 이러면 Selenium 로그인 자동화에서 흔한 CAPTCHA와 SMS 2단계 인증을 피할 수 있어요.
필요한 쿠키는 다음 7개예요.
| 쿠키 이름 | 용도 |
|---|---|
session-id | 회전하는 세션 식별자 |
session-id-time | 세션 타임스탬프 |
session-token | 회전하는 세션 토큰 |
ubid-main | 사용자 브라우징 식별자 |
at-main | 주요 인증 토큰 |
sess-at-main | 세션 범위 인증 정보 |
x-main | 사용자 이메일 기반 식별자 |
Chrome DevTools에서 쿠키 추출하는 방법
- Chrome에서 amazon.com에 로그인해요
- DevTools를 엽니다(F12 또는 우클릭 → 검사)
- Application → Storage → Cookies →
https://www.amazon.com으로 이동해요 - 표에 나온 각 쿠키 이름을 찾아 값을 복사해요
- Python에서 쓸 수 있도록 세미콜론으로 구분된 문자열 형식으로 정리해요
세션 설정은 이렇게 해요.
import requests
session = requests.Session()
# 여기에 쿠키 값을 붙여넣으세요
cookies = {
"session-id": "YOUR_SESSION_ID",
"session-id-time": "YOUR_SESSION_ID_TIME",
"session-token": "YOUR_SESSION_TOKEN",
"ubid-main": "YOUR_UBID_MAIN",
"at-main": "YOUR_AT_MAIN",
"sess-at-main": "YOUR_SESS_AT_MAIN",
"x-main": "YOUR_X_MAIN",
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
}
session.cookies.update(cookies)
session.headers.update(headers)
중요: 모든 요청에서 같은 session 객체를 재사용하세요. 그래야 쿠키가 일관되게 유지되고 실제 브라우저 세션처럼 보여요. 스크래핑 부하가 아주 크지 않다면 쿠키는 보통 며칠에서 몇 주는 버텨요. 그러다 다시 로그인 페이지로 튕기기 시작하면, 브라우저에서 쿠키를 새로 복사하면 돼요.
.com이 아닌 마켓플레이스는 쿠키 이름이 조금씩 달라요. amazon.de는 at-main 대신 at-acbde, amazon.co.uk는 at-acbuk를 써요. 마켓플레이스마다 별도 세션이 필요해요.
2단계: 요청을 만들고 BeautifulSoup으로 리뷰 HTML 파싱하기
Amazon 리뷰 URL 형식은 이래요.
https://www.amazon.com/product-reviews/{ASIN}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1
핵심 함수예요.
from bs4 import BeautifulSoup
import time, random
def get_soup(session, url):
time.sleep(random.uniform(2, 5)) # 예의 있는 딜레이
response = session.get(url, timeout=15)
# 로그인 벽 감지
if "ap_email" in response.text or "Amazon Sign-In" in response.text:
raise Exception("로그인 벽이 감지되었습니다 — 쿠키를 새로 갱신하세요")
if response.status_code != 200:
raise Exception(f"HTTP {response.status_code}")
return BeautifulSoup(response.text, "lxml")
작지만 쏠쏠한 팁이 있어요. 리뷰 페이지를 곧장 열기 전에 상품 페이지부터 들르세요. 그러면 세션에 자연스러운 브라우징 흐름이 생겨요.
# 먼저 상품 페이지 방문(실제 브라우징처럼 보이게 함)
product_url = f"https://www.amazon.com/dp/{asin}"
session.get(product_url, timeout=15)
time.sleep(random.uniform(1, 3))
# 그 다음 리뷰 페이지 요청
reviews_url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1"
soup = get_soup(session, reviews_url)
3단계: 안정적인 셀렉터로 리뷰 데이터 추출하기(CSS 클래스에 의존하지 마세요)
2022~2023년 튜토리얼이 대부분 무너지는 지점이 여기예요. Amazon은 CSS 클래스명을 난독화해서 주기적으로 바꿔요. 어떤 개발자는 포럼에서 이렇게 푸념했죠. "span 태그 클래스 이름에 공통 패턴이 하나도 없었다."
해결책은 Amazon 리뷰 요소에 붙은 data-hook 속성을 쓰는 거예요. 놀랄 만큼 안정적이에요. Amazon 자체 프론트엔드가 의존하는 의미 기반 식별자라, 함부로 바뀌지 않거든요.
| 리뷰 항목 | 안정적인 셀렉터 (data-hook) | 취약한 셀렉터 (class) |
|---|---|---|
| 리뷰 본문 | [data-hook="review-body"] | .review-text-content (변경됨) |
| 별점 | [data-hook="review-star-rating"] | .a-icon-alt (모호함) |
| 리뷰 제목 | [data-hook="review-title"] | .review-title (가끔만) |
| 작성자 이름 | span.a-profile-name | 비교적 안정적 |
| 리뷰 날짜 | [data-hook="review-date"] | .review-date (지역별 차이) |
| 인증 구매 여부 | [data-hook="avp-badge"] | span.a-size-mini |
data-hook 셀렉터를 쓴 추출 코드예요.
import re
def extract_reviews(soup):
reviews = []
review_divs = soup.select('[data-hook="review"]')
for div in review_divs:
# 별점
rating_el = div.select_one('[data-hook="review-star-rating"]')
rating = None
if rating_el:
rating_text = rating_el.get_text(strip=True)
match = re.search(r'(\d\.?\d?)', rating_text)
if match:
rating = float(match.group(1))
# 제목
title_el = div.select_one('[data-hook="review-title"]')
title = title_el.get_text(strip=True) if title_el else ""
# 본문
body_el = div.select_one('[data-hook="review-body"]')
body = body_el.get_text(strip=True) if body_el else ""
# 작성자
author_el = div.select_one('span.a-profile-name')
author = author_el.get_text(strip=True) if author_el else ""
# 날짜 및 국가
date_el = div.select_one('[data-hook="review-date"]')
date_text = date_el.get_text(strip=True) if date_el else ""
# 형식: "Reviewed in the United States on January 15, 2025"
country_match = re.search(r'Reviewed in (.+?) on', date_text)
date_match = re.search(r'on (.+)$', date_text)
country = country_match.group(1) if country_match else ""
date = date_match.group(1) if date_match else ""
# 인증 구매
verified_el = div.select_one('[data-hook="avp-badge"]')
verified = bool(verified_el)
reviews.append({
"author": author,
"rating": rating,
"title": title,
"content": body,
"date": date,
"country": country,
"verified": verified,
})
return reviews
저는 이 셀렉터 조합을 여러 ASIN에 몇 달째 돌리고 있는데, data-hook 속성은 한 번도 안 바뀌었어요. 반면 CSS 클래스는 같은 기간에 최소 두 번은 바뀌었어요.
4단계: 페이지네이션과 Amazon의 10페이지 제한 처리하기
Amazon은 pageNumber 파라미터를 별점·정렬 조합마다 10페이지로 끊어요. 페이지당 10개 리뷰 기준 최대 약 100개죠. 10페이지를 넘으면 "다음 페이지" 버튼이 사라져요.
기본 페이지네이션 루프예요.
all_reviews = []
for page in range(1, 11):
url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber={page}"
soup = get_soup(session, url)
page_reviews = extract_reviews(soup)
if not page_reviews:
break # 이 페이지에 더 이상 리뷰가 없음
all_reviews.extend(page_reviews)
print(f"Page {page}: {len(page_reviews)} reviews")
10페이지보다 더 많은 Amazon 리뷰를 가져오는 방법
우회법은 필터 버킷을 활용하는 거예요. filterByStar와 sortBy 조합마다 독립적인 10페이지 창이 생겨요.
별점 필터 값: one_star, two_star, three_star, four_star, five_star
정렬 값: recent, helpful(기본값)
별점 필터 5개 × 정렬 2개를 조합하면 제품당 최대 100페이지, 즉 1,000개 리뷰까지 닿을 수 있어요. 별점 분포가 고르지 않은 상품이라면 전체 리뷰에 꽤 근접하는 경우도 많아요.
star_filters = ["one_star", "two_star", "three_star", "four_star", "five_star"]
sort_orders = ["recent", "helpful"]
all_reviews = []
seen_titles = set() # 간단한 중복 제거
for star in star_filters:
for sort in sort_orders:
for page in range(1, 11):
url = (
f"https://www.amazon.com/product-reviews/{asin}"
f"?filterByStar={star}&sortBy={sort}&pageNumber={page}"
)
soup = get_soup(session, url)
page_reviews = extract_reviews(soup)
if not page_reviews:
break
for review in page_reviews:
# 제목 + 작성자 조합으로 중복 제거
key = (review["title"], review["author"])
if key not in seen_titles:
seen_titles.add(key)
all_reviews.append(review)
print(f"[{star}/{sort}] Page {page}: {len(page_reviews)} reviews")
print(f"Total unique reviews: {len(all_reviews)}")
버킷끼리 겹치는 리뷰가 있어서 중복 제거는 필수예요. 저는 리뷰 제목 + 작성자 이름 조합을 빠른 키로 써요. 완벽하진 않아도 중복 대부분은 걸러져요.
5단계: 봇 차단 피하기(회전, 속도 제한, 재시도)
Amazon은 AWS WAF Bot Control을 쓰는데, 요즘 훨씬 더 공격적이에요. User-Agent만 바꾸거나 딜레이만 넣는 단일 대응으론 이제 어림없어요.
| 기법 | 구현 방법 |
|---|---|
| User-Agent 회전 | 실제 브라우저 문자열 10개 이상에서 무작위 선택 |
| 지수 백오프 | 503 응답 시 2초 → 4초 → 8초로 재시도 지연 |
| 요청 속도 제한 | 페이지 사이에 random.uniform(2, 5)초 대기 |
| 프록시 회전 | 주거용 프록시를 순환 사용 |
| 세션 지문 일관성 | 세션마다 쿠키와 헤더를 일관되게 유지 |
| TLS 스푸핑 | 운영 환경에서는 기본 requests 대신 curl_cffi 사용 |
운영용 재시도 래퍼는 이렇게 만들 수 있어요.
import time, random
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 15_7_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
]
def scrape_with_retries(session, url, max_retries=3):
for attempt in range(max_retries):
try:
session.headers["User-Agent"] = random.choice(USER_AGENTS)
time.sleep(random.uniform(2, 5))
response = session.get(url, timeout=15)
# 차단 감지
if "validateCaptcha" in response.url or "Robot Check" in response.text:
wait = (2 ** attempt) * 5
print(f"CAPTCHA가 감지되었습니다. {wait}초 대기 중...")
time.sleep(wait)
continue
if response.status_code in (429, 503):
wait = (2 ** attempt) * 2
print(f"요청 제한 감지({response.status_code}). {wait}초 대기 중...")
time.sleep(wait)
continue
if "ap_email" in response.text:
raise Exception("로그인 벽 — 쿠키가 만료되었습니다")
return BeautifulSoup(response.text, "lxml")
except Exception as e:
if attempt == max_retries - 1:
raise
print(f"시도 {attempt + 1} 실패: {e}")
return None
프록시 얘기도 빼놓을 수 없어요. Amazon은 AWS, GCP, Azure, DigitalOcean 같은 데이터센터 IP 대역을 네트워크 레벨에서 차단해요. 수백 페이지 넘게 긁는다면 사실상 주거용 프록시가 필요해요. 사용량에 따라 월 $50200(약 6만 8천27만 원) 이상을 잡아야 해요. 반면 소규모 프로젝트(하루 100 요청 이하)라면 개인 회선에서 조심스레 속도만 줄여도 충분한 경우가 많아요.
Amazon은 TLS 지문도 봐요. Python 기본 requests는 WAF가 미리 차단하는 잘 알려진 JA3 해시를 갖고 있어요. 운영용 스크래퍼라면 실제 브라우저의 TLS 스택을 흉내 내는 curl_cffi를 고려해 보세요. 튜토리얼 수준(수백 페이지)이라면 헤더만 잘 맞춘 requests로도 대체로 충분해요.
6단계: 스크래핑한 Amazon 리뷰를 CSV나 Excel로 내보내기
리뷰를 모았다면 pandas로 바로 쓸 수 있는 형식으로 저장하면 돼요.
import pandas as pd
df = pd.DataFrame(all_reviews)
df.to_csv("amazon_reviews.csv", index=False)
print(f"Exported {len(df)} reviews to amazon_reviews.csv")
예시 출력이에요.
| author | rating | title | content | date | country | verified |
|---|---|---|---|---|---|---|
| Sarah M. | 5.0 | 올해 최고의 구매 | 배터리가 하루 종일 가고, 화면도 정말 예뻐요... | January 15, 2025 | the United States | True |
| Mike T. | 2.0 | 2주 만에 실망 | 충전 포트가 작동하지 않기 시작했어요... | February 3, 2025 | the United States | True |
| Priya K. | 4.0 | 가격 대비 훌륭함 | 필요한 기능은 다 있고, 무거운 앱에서는 약간 느립니다... | March 10, 2025 | the United States | False |
Excel로 내보내려면 df.to_excel("amazon_reviews.xlsx", index=False)를 쓰면 돼요(openpyxl 필요).
Google Sheets로 보내려면 gspread 라이브러리가 있긴 한데, Google Cloud 설정을 8단계 넘게 거쳐야 해요. 프로젝트 생성, API 두 개 활성화, 서비스 계정 자격 증명 발급, 시트 공유까지. 스크래핑보다 설정이 더 일처럼 느껴진다면, 맞아요. (이럴 때 Thunderbit처럼 클릭 한 번에 Google Sheets로 보내는 도구가 갑자기 무척 매력적으로 보여요.)
보너스: Python 5줄로 리뷰에 감성 분석 추가하기
대부분의 스크래핑 튜토리얼은 CSV 내보내기에서 끝나요. 하지만 감성을 점수화해야 원시 데이터가 비즈니스 결정으로 바뀌어요.
가장 빠른 기준선은 TextBlob이에요.
from textblob import TextBlob
df["sentiment"] = df["content"].apply(lambda x: TextBlob(str(x)).sentiment.polarity)
그러면 리뷰마다 -1.0(매우 부정)부터 +1.0(매우 긍정)까지 polarity 점수가 나와요. 예시 출력이에요.
| content (축약) | rating | sentiment |
|---|---|---|
| "배터리가 하루 종일 가고, 화면도 정말 예뻐요..." | 5.0 | 0.65 |
| "충전 포트가 ... 후부터 작동하지 않았어요." | 2.0 | -0.40 |
| "필요한 기능은 다 있고, ...에서는 약간 느려요." | 4.0 | 0.25 |
| "완전 쓰레기예요. 바로 반품했습니다." | 1.0 | -0.75 |
| "그냥 그래요. 특별하진 않지만 작동은 합니다." | 3.0 | 0.10 |
가장 흥미로운 행은 별점과 감성이 어긋나는 경우예요. 3점인데 문장은 긍정적이거나, 5점인데 표현은 부정적인 경우죠. 이런 어긋남은 별점만으로는 놓치는 미묘한 고객 속내를 드러내요.

운영 수준의 정확도를 원한다면 Hugging Face Transformers를 추천해요. VADER는 상품 리뷰가 아니라 트윗으로 훈련됐고, 트랜스포머 모델은 어휘 기반 도구보다 리뷰 분류에서 98% 넘는 정확도를 보여요. nlptown/bert-base-multilingual-uncased-sentiment 모델은 1~5점 별점을 직접 예측하기도 해요.
from transformers import pipeline
clf = pipeline("sentiment-analysis",
model="nlptown/bert-base-multilingual-uncased-sentiment")
df["predicted_stars"] = df["content"].apply(
lambda x: int(clf(str(x)[:512])[0]["label"][0])
)
Amazon 리뷰는 J자형 분포를 따라요. 5점에 큰 봉우리, 1점에 작은 봉우리, 중간은 푹 꺼지죠. 그래서 평균 별점은 실제 제품 품질을 대변하는 지표로 종종 부정확해요. 1점군을 따로 떼어 반복되는 주제를 파 보세요. 보통 고칠 수 있는 결함 하나가 그 안에 숨어 있어요.
솔직한 비교: 직접 만든 Python vs 유료 스크래핑 API vs Thunderbit
저도 Amazon용 Python 스크래퍼를 직접 굴려 왔는데, 솔직히 자주 깨져요. 셀렉터가 바뀌고, 쿠키가 만료되고, Amazon이 새 봇 탐지 레이어를 깔면 어느새 토요일 오전이 디버깅으로 날아가요. 포럼 사용자들 불만도 비슷해요. "지난달엔 됐던" DIY 스크립트를 이제 계속 손봐야 한다는 거죠.
세 가지 접근법을 비교하면 이래요.
| 기준 | DIY Python (BS4/Selenium) | 유료 스크래핑 API | Thunderbit (노코드) |
|---|---|---|---|
| 설정 시간 | 1~3시간 | 30분(API 키만 있으면 됨) | 2분 |
| 비용 | 무료(+ 프록시 비용) | 월 $50~200+ | 무료 플랜 있음 |
| 로그인 벽 처리 | 쿠키 수동 관리 | 대개 자동 처리 | 자동 처리 |
| 유지보수 | 높음(셀렉터가 자주 깨짐) | 낮음(제공사가 유지보수) | 없음(AI가 적응) |
| 페이지네이션 | 직접 코드 작성 필요 | 내장됨 | 내장됨 |
| 다국가 지원 | 도메인별로 세션 분리 필요 | 대개 지원 | 브라우저 기반이라 로케일 활용 |
| 감성 분석 | 직접 코드 추가 | 일부 포함 | Sheets로 내보내 어디서든 분석 가능 |
| 적합한 경우 | 학습, 완전한 제어 필요 | 대규모/운영용 파이프라인 | 빠른 데이터 추출, 비개발팀 |
Python은 완전한 제어가 되고, 웹 스크래핑이 안에서 어떻게 도는지 배우기에도 가장 좋아요. 하지만 비용보다 가동 안정성이 중요한 운영 파이프라인이라면 유료 API(ScrapingBee, Oxylabs, Bright Data 등)가 더 맞을 수 있어요. 그리고 개발 부담 없이 리뷰 데이터만 필요하다면 — 예를 들어 이커머스 운영팀이 경쟁사 제품을 매주 모니터링하거나, 마케팅팀이 광고 카피용 고객 표현을 뽑아야 한다면 — 세 번째 길이 있어요.
Thunderbit으로 Amazon 리뷰 스크래핑하기(코드 없이, 유지보수 없이)
저희는 Python 스크래퍼를 직접 떠안는 게 너무 번거로운 상황을 위해 Thunderbit을 만들었어요. 흐름은 아주 간단해요.
- Thunderbit Chrome 확장 프로그램을 설치하세요
- 브라우저에서 Amazon 상품 리뷰 페이지로 이동하세요(이미 로그인돼 있으니 로그인 벽은 문제가 안 돼요)
- "AI 필드 제안"을 클릭해요 — Thunderbit이 페이지를 읽고 작성자, 평점, 제목, 리뷰 텍스트, 날짜, 인증 구매 같은 열을 제안해요
- "스크래핑"을 클릭해요 — 내장 페이지네이션과 함께 데이터가 바로 떨어져요
- Excel, Google Sheets, Airtable, Notion으로 내보내기 해요
가장 큰 장점은 Thunderbit의 AI가 매번 페이지 구조를 새로 읽는다는 점이에요. 유지보수할 CSS 셀렉터도, 쿠키 관리도, 봇 차단 코드와 씨름할 일도 없어요. Amazon이 HTML을 바꿔도 AI가 알아서 따라가요. 코드 없이 프로그래밍 방식 접근이 필요하다면 Thunderbit의 Extract API도 있어요. AI 기반 필드 감지로 구조화된 데이터를 API로 뽑을 수 있어서 셀렉터 유지보수가 필요 없어요.
Amazon 데이터 관련 더 깊은 내용은 Amazon 상품 및 리뷰 스크래핑 방법과 Amazon 판매 데이터 추출 및 분석 가이드를 참고하세요.
Python으로 Amazon 리뷰를 대량 스크래핑할 때의 팁
여러 ASIN을 대상으로 리뷰를 모은다면, 아래 습관이 나중에 큰 수고를 덜어 줘요.
- ASIN을 배치로 나누고 상품 사이에도 딜레이를 넣으세요(페이지 사이만 쉬는 걸로는 부족해요). 저는 ASIN 간 10~15초쯤 멈춰요.
- 중복 제거를 빡세게 하세요. 여러 별점 필터와 정렬 조합을 합치면 겹치는 리뷰가 생겨요.
(title, author, date)튜플을 중복 제거 키로 쓰면 좋아요. - 실패 로그를 남기세요. 어떤 ASIN + 페이지 + 필터 조합이 실패했는지 적어 두면, 전체를 다시 긁지 않고도 재시도할 수 있어요.
- 대형 프로젝트는 DB에 저장하세요. CSV를 계속 불리는 것보다 간단한 SQLite 데이터베이스가 훨씬 확장성이 좋아요.
import sqlite3
conn = sqlite3.connect("reviews.db")
df.to_sql("reviews", conn, if_exists="append", index=False)
- 반복 수집을 예약하세요. 계속 모니터링해야 한다면 cron 작업을 걸거나 Thunderbit의 Scheduled Scraper 기능을 쓰세요. URL과 일정만 설명해 두면 서버 없이 나머지를 알아서 처리해요.
추가 방법은 최고의 자동화 웹 스크래핑 도구와 웹사이트 데이터를 Excel로 스크래핑하는 방법 글에서 더 확인할 수 있어요.
법적·윤리적 고려사항에 대한 짧은 안내
Amazon의 이용 약관은 Amazon 서비스 접근을 위해 "로봇, 스파이더, 스크래퍼 또는 기타 자동화 수단을 사용하는 행위"를 대놓고 금지해요. 다만 최근 미국 판례는 공개 데이터 스크래퍼에 우호적인 편이었어요. Meta Platforms v. Bright Data(2024년 1월) 사건에서 연방법원은 로그인한 "사용자"가 아니라면, 공개적으로 접근 가능한 데이터를 스크래핑하는 게 서비스 약관 위반이 아니라고 봤어요.
중요한 차이는 로그인 뒤 데이터를 긁는 경우예요(이 튜토리얼이 다루는 범위죠). 이 경우엔 계정을 만들며 Amazon ToS에 동의한 셈이라 계약법 영역으로 들어가요. 로그인 벽 뒤 데이터를 긁는 건 공개 대표 리뷰만 긁는 것보다 법적 위험이 더 커요.
실무 지침으로는, 스크래핑한 데이터를 상업적으로 재배포하지 말 것, 공개 표시 범위를 넘어 개인 사용자 데이터를 수집하지 말 것, robots.txt를 존중할 것, 대규모·상업적 사용 시 법률 자문을 받을 것이 있어요. 이건 법률 자문이 아니에요. 더 자세한 법적 배경은 웹 스크래핑의 법적 쟁점 개요를 참고하세요.
결론: Python으로 Amazon 리뷰를 스크래핑하거나, 아예 코드를 건너뛰기
이 글에서 다룬 내용을 짧게 정리하면 이래요.
- 로그인 벽은 실재하지만 쿠키 기반 인증으로 풀 수 있어요. 브라우저에서 쿠키 7개를 복사해
requests.Session()에 넣으세요 - 몇 주마다 깨지는 CSS 클래스 대신
data-hook셀렉터를 쓰세요 - 별점 필터와 정렬을 조합하면 10페이지 한도를 넘어 제품당 500개 이상 리뷰에 닿을 수 있어요
- TextBlob으로 빠른 기준선을 잡거나, Hugging Face Transformers로 운영 수준 감성 분석을 더하세요
- 봇 차단 대응도 챙기세요. 속도 제한, User-Agent 회전, 지수 백오프, 규모가 크면 주거용 프록시까지 필요해요
Python은 완전한 제어를 주고, 내부 구조를 이해하는 데 가장 좋아요. 하지만 목표가 "운영용 데이터 파이프라인 구축"이 아니라 "금요일까지 경쟁사 리뷰를 스프레드시트에 넣기"라면, 커스텀 스크래퍼의 유지보수 부담은 굳이 떠안을 가치가 없을 수 있어요.
Thunderbit은 인증, 셀렉터, 페이지네이션, 내보내기를 클릭 몇 번으로 처리해요. 무료 플랜을 한번 써 보고 워크플로에 맞는지 확인해 보세요. Amazon이 봇 차단을 계속 조이는 만큼, 실시간으로 적응하는 AI 도구는 점점 있으면 좋은 옵션이 아니라 필수에 가까워지고 있어요.
스크래핑 워크플로의 영상 안내가 필요하다면 Thunderbit YouTube 채널도 확인해 보세요.
자주 묻는 질문
1. 로그인 없이 Amazon 리뷰를 스크래핑할 수 있나요?
네, 다만 상품 상세 페이지(/dp/{ASIN}/)에 뜨는 "대표 리뷰" 약 8개 정도만 가능해요. 정렬, 필터, 페이지네이션이 되는 전체 리뷰 페이지는 2024년 말 기준 인증이 필요해요. 대부분의 비즈니스 활용에선 로그인 벽을 넘어야 해요.
2. Amazon 리뷰 스크래핑은 합법인가요?
Amazon 이용 약관은 자동화된 스크래핑을 금지해요. 다만 최근 미국 판례(Meta v. Bright Data, 2024; hiQ v. LinkedIn)는 공개적으로 접근 가능한 데이터 스크래핑을 지지해요. 로그인 뒤 데이터를 긁으면 Amazon ToS에 동의한 상태라 법적 위험이 더 커요. 상업적 사용 전엔 법률 자문을 받으세요.
3. 상품당 Amazon 리뷰를 몇 개까지 가져올 수 있나요?
Amazon은 별점·정렬 조합마다 리뷰 페이지를 10페이지로 제한해요. 별점 필터 5개 × 정렬 2개를 다 쓰면 상품당 최대 100페이지(대략 1,000개 리뷰)까지 닿을 수 있어요. 키워드 필터를 쓰면 이론상 한도는 더 높아지지만 중복도 훨씬 많아져요.
4. Amazon 리뷰 스크래핑에 가장 좋은 Python 라이브러리는 무엇인가요?
정적 HTML 파싱엔 requests + BeautifulSoup 조합이 가장 흔하고 안정적이에요. JavaScript 렌더링이 필요하면 Selenium이 유용해요. 로그인 벽과 페이지네이션을 자동으로 처리하는 노코드 대안이 필요하다면 Thunderbit을 써 보세요.
5. Amazon 스크래핑 시 차단을 피하려면 어떻게 해야 하나요?
실제 브라우저 문자열 10개 이상에서 User-Agent를 회전시키고, 요청 사이에 2~5초 무작위 딜레이를 넣고, 429/503 오류엔 지수 백오프를 적용하고, 규모가 크면 주거용 프록시를 쓰세요(데이터센터 IP는 미리 차단되는 경우가 많아요). 요청마다 세션 쿠키도 일관되게 유지해야 해요. 유지보수 없이 가고 싶다면 Thunderbit이 브라우저 세션을 통해 봇 차단 대응을 자동으로 처리해요.
더 알아보기


