내 Amazon 리뷰 스크래퍼는 6주 동안 아주 멀쩡하게 돌아갔습니다. 그런데 어느 날 아침, 응답은 200 OK였는데 화면에는 아무것도 없더군요. 오류도 없고, CAPTCHA도 없고, 수백 개의 리뷰가 있어야 할 자리에 텅 빈 HTML만 덩그러니 남아 있었습니다.
이런 상황이 낯설지 않다면, 당신만 그런 게 아닙니다. 2025년 말 Amazon은 전체 리뷰 페이지를 로그인 뒤로 막기 시작했고, 그 여파로 엄청난 수의 파이썬 스크래핑 스크립트가 하룻밤 사이에 무너졌습니다. 저는 지난 몇 달 동안 에서 AI 스크래퍼를 만들면서 동시에 개인용 파이썬 리뷰 수집 파이프라인도 같이 유지해 왔기 때문에, 제 스크립트가 처음 멈췄을 때 정말 필요했던 가이드를 직접 정리해 보기로 했습니다. 이 글에서는 실제로 잘 돌아가는 방법을 다룹니다. 쿠키 기반 인증, Amazon의 CSS 난독화를 버텨내는 안정적인 셀렉터, 10페이지 페이지네이션 제한 우회법, 봇 차단 대응, 그리고 원본 리뷰 텍스트를 실질적인 비즈니스 인사이트로 바꾸는 감성 분석까지요. 그리고 읽다가 “이 코드를 내가 굳이 다 유지보수해야 하나?” 싶다면, 같은 작업을 Python 없이 약 2분 만에 끝내는 방식도 같이 보여드리겠습니다.
Amazon 리뷰 스크래핑이란 무엇이며, 왜 중요한가?
Amazon 리뷰 스크래핑은 상품 페이지에서 별점, 리뷰 본문, 작성자 이름, 날짜, 인증 구매 배지 같은 고객 리뷰 데이터를 프로그램으로 뽑아내는 작업입니다. Amazon이 2010년에 다시 되돌리지 않았기 때문에, 이 데이터에 프로그래밍 방식으로 접근하려면 사실상 웹 스크래핑이 유일합니다.
숫자도 이걸 뒷받침합니다. , . 상품 페이지에 리뷰를 단 5개만 보여줘도 할 수 있습니다. 리뷰 감성을 체계적으로 분석하는 기업은 는 결과도 있습니다. 이건 막연한 데이터 과학 얘기가 아닙니다. 경쟁 정보, 제품 개선 신호, 마케팅 문구가 Amazon 서버 안에 텍스트 형태로 그대로 쌓여 있는 셈이죠.
왜 Python으로 Amazon 리뷰를 스크래핑할까?
이 작업에서는 Python이 여전히 가장 많이 선택되는 언어입니다. 에서 가장 선호되는 언어 1위였고, requests, BeautifulSoup, pandas, Scrapy 같은 생태계 덕분에 전업 개발자가 아니어도 웹 스크래핑을 비교적 쉽게 시작할 수 있습니다.
팀마다 이 데이터를 활용하는 방식도 제각각입니다.
| 팀 | 활용 사례 | 추출하는 정보 |
|---|---|---|
| 제품 / R&D | 반복적으로 나오는 불만 파악, 개선 우선순위 설정 | 1~2점 리뷰 텍스트, 키워드 빈도 |
| 영업 | 경쟁사 제품 반응 모니터링 | 평점, 리뷰 수 추이 |
| 마케팅 | 광고 문구에 쓸 고객 언어 확보 | 긍정적인 리뷰 표현, 기능 언급 |
| 이커머스 운영 | 자사 제품 감성 흐름 추적 | 별점 분포, 인증 구매 비율 |
| 시장 조사 | 카테고리 리더 간 기능 비교 | 여러 ASIN의 리뷰 데이터셋 |
한 주방용품 브랜드는 , 제품을 다시 설계한 뒤 60일 만에 #1 베스트셀러 자리를 되찾았습니다. 한 피트니스 트래커 회사는 라텍스 알레르기 문제를 찾아냈고, 저자극 버전을 출시한 뒤 반품률을 40% 줄였습니다. 이런 ROI라면 개발 공수를 들일 이유가 충분하죠.
로그인 벽: 왜 Amazon 리뷰 스크래퍼가 멈췄을까?
2024년 11월 14일, . 이 변경은 과 전반에서 확인됐습니다. 시크릿 창에서 /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)는 인증 없이도 대략 를 보여줍니다. 이 리뷰들은 Amazon 알고리즘이 골라주는 것이어서 간단한 감성 확인에는 유용하지만, 정렬이나 필터, 페이지네이션은 지원하지 않습니다.
전체 리뷰 페이지(/product-reviews/{ASIN}/)는 최신순 정렬, 별점 필터링, 수백 개 리뷰에 대한 페이지 넘김까지 포함하지만, 로그인해야 접근할 수 있습니다.
짧게 상태만 훑어보고 싶다면 상품 페이지를 스크래핑하면 됩니다. 수백 개나 수천 개가 필요하다면 인증 처리가 필요합니다.
시작 전 준비사항: Python 환경과 라이브러리
코드를 쓰기 전에 준비해야 할 것은 아래와 같습니다.
- 난이도: 중급 (Python에 익숙하고, 기본 HTML 구조를 이해하면 충분)
- 소요 시간: 전체 파이프라인 약 45분, 기본 스크래핑은 약 10분
- 필요한 것: Python 3.8+, Chrome 브라우저, 유효한 Amazon 계정
핵심 라이브러리를 설치합니다.
1pip install requests beautifulsoup4 lxml pandas textblob
선택 사항(고급 감성 분석용):
1pip 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에서 쓸 수 있도록 세미콜론으로 구분된 문자열 형식으로 정리합니다
세션 설정은 이렇게 합니다.
1import requests
2session = requests.Session()
3# 여기에 쿠키 값을 붙여넣으세요
4cookies = {
5 "session-id": "YOUR_SESSION_ID",
6 "session-id-time": "YOUR_SESSION_ID_TIME",
7 "session-token": "YOUR_SESSION_TOKEN",
8 "ubid-main": "YOUR_UBID_MAIN",
9 "at-main": "YOUR_AT_MAIN",
10 "sess-at-main": "YOUR_SESS_AT_MAIN",
11 "x-main": "YOUR_X_MAIN",
12}
13headers = {
14 "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",
15 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
16 "Accept-Language": "en-US,en;q=0.5",
17}
18session.cookies.update(cookies)
19session.headers.update(headers)
중요: 모든 요청에서 같은 session 객체를 재사용하세요. 그래야 쿠키가 일관되게 유지되고, 실제 브라우저 세션처럼 보입니다. 스크래핑 부하가 아주 크지 않다면 쿠키는 보통 며칠에서 몇 주 정도 유지됩니다. 그런데 다시 로그인 페이지로 리디렉션되기 시작하면, 브라우저에서 쿠키를 새로 복사해야 합니다.
.com이 아닌 마켓플레이스에서는 쿠키 이름이 조금씩 달라집니다. 예를 들어 amazon.de는 at-main 대신 at-acbde를 쓰고, amazon.co.uk는 at-acbuk를 사용합니다. 각 마켓플레이스는 별도의 세션이 필요합니다.
2단계: 요청을 만들고 BeautifulSoup으로 리뷰 HTML 파싱하기
Amazon 리뷰 URL 형식은 다음과 같습니다.
1https://www.amazon.com/product-reviews/{ASIN}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1
핵심 함수는 다음과 같습니다.
1from bs4 import BeautifulSoup
2import time, random
3def get_soup(session, url):
4 time.sleep(random.uniform(2, 5)) # 예의 있는 딜레이
5 response = session.get(url, timeout=15)
6 # 로그인 벽 감지
7 if "ap_email" in response.text or "Amazon Sign-In" in response.text:
8 raise Exception("로그인 벽이 감지되었습니다 — 쿠키를 새로 갱신하세요")
9 if response.status_code != 200:
10 raise Exception(f"HTTP {response.status_code}")
11 return BeautifulSoup(response.text, "lxml")
작지만 꽤 유용한 팁이 하나 있습니다. 리뷰 페이지를 바로 열기 전에 먼저 상품 페이지를 방문하세요. 그러면 세션에서 자연스러운 브라우징 패턴이 만들어집니다.
1# 먼저 상품 페이지 방문(실제 브라우징처럼 보이게 함)
2product_url = f"https://www.amazon.com/dp/{asin}"
3session.get(product_url, timeout=15)
4time.sleep(random.uniform(1, 3))
5# 그 다음 리뷰 페이지 요청
6reviews_url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber=1"
7soup = 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 셀렉터를 사용한 추출 코드는 다음과 같습니다.
1import re
2def extract_reviews(soup):
3 reviews = []
4 review_divs = soup.select('[data-hook="review"]')
5 for div in review_divs:
6 # 별점
7 rating_el = div.select_one('[data-hook="review-star-rating"]')
8 rating = None
9 if rating_el:
10 rating_text = rating_el.get_text(strip=True)
11 match = re.search(r'(\d\.?\d?)', rating_text)
12 if match:
13 rating = float(match.group(1))
14 # 제목
15 title_el = div.select_one('[data-hook="review-title"]')
16 title = title_el.get_text(strip=True) if title_el else ""
17 # 본문
18 body_el = div.select_one('[data-hook="review-body"]')
19 body = body_el.get_text(strip=True) if body_el else ""
20 # 작성자
21 author_el = div.select_one('span.a-profile-name')
22 author = author_el.get_text(strip=True) if author_el else ""
23 # 날짜 및 국가
24 date_el = div.select_one('[data-hook="review-date"]')
25 date_text = date_el.get_text(strip=True) if date_el else ""
26 # 형식: "Reviewed in the United States on January 15, 2025"
27 country_match = re.search(r'Reviewed in (.+?) on', date_text)
28 date_match = re.search(r'on (.+)$', date_text)
29 country = country_match.group(1) if country_match else ""
30 date = date_match.group(1) if date_match else ""
31 # 인증 구매
32 verified_el = div.select_one('[data-hook="avp-badge"]')
33 verified = bool(verified_el)
34 reviews.append({
35 "author": author,
36 "rating": rating,
37 "title": title,
38 "content": body,
39 "date": date,
40 "country": country,
41 "verified": verified,
42 })
43 return reviews
저는 이 셀렉터 조합을 여러 ASIN에 대해 몇 달째 돌리고 있는데, data-hook 속성은 한 번도 바뀌지 않았습니다. 반면 CSS 클래스는 같은 기간에 적어도 두 번은 바뀌었습니다.
4단계: 페이지네이션과 Amazon의 10페이지 제한 처리하기
Amazon은 pageNumber 파라미터를 별점 및 정렬 조합마다 10페이지, 즉 페이지당 10개 리뷰 기준 최대 약 100개로 제한합니다. 10페이지가 지나면 “다음 페이지” 버튼이 사라집니다.
기본 페이지네이션 루프는 다음과 같습니다.
1all_reviews = []
2for page in range(1, 11):
3 url = f"https://www.amazon.com/product-reviews/{asin}/ref=cm_cr_arp_d_viewopt_srt?sortBy=recent&pageNumber={page}"
4 soup = get_soup(session, url)
5 page_reviews = extract_reviews(soup)
6 if not page_reviews:
7 break # 이 페이지에 더 이상 리뷰가 없음
8 all_reviews.extend(page_reviews)
9 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개 리뷰까지 접근할 수 있습니다. 별점 분포가 고르지 않은 상품이라면 전체 리뷰에 꽤 가까운 수준까지 확보되는 경우도 많습니다.
1star_filters = ["one_star", "two_star", "three_star", "four_star", "five_star"]
2sort_orders = ["recent", "helpful"]
3all_reviews = []
4seen_titles = set() # 간단한 중복 제거
5for star in star_filters:
6 for sort in sort_orders:
7 for page in range(1, 11):
8 url = (
9 f"https://www.amazon.com/product-reviews/{asin}"
10 f"?filterByStar={star}&sortBy={sort}&pageNumber={page}"
11 )
12 soup = get_soup(session, url)
13 page_reviews = extract_reviews(soup)
14 if not page_reviews:
15 break
16 for review in page_reviews:
17 # 제목 + 작성자 조합으로 중복 제거
18 key = (review["title"], review["author"])
19 if key not in seen_titles:
20 seen_titles.add(key)
21 all_reviews.append(review)
22 print(f"[{star}/{sort}] Page {page}: {len(page_reviews)} reviews")
23print(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 사용 |
운영 환경에 맞는 재시도 래퍼는 아래처럼 만들 수 있습니다.
1import time, random
2USER_AGENTS = [
3 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
4 "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",
5 "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0",
6 "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",
7 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
8]
9def scrape_with_retries(session, url, max_retries=3):
10 for attempt in range(max_retries):
11 try:
12 session.headers["User-Agent"] = random.choice(USER_AGENTS)
13 time.sleep(random.uniform(2, 5))
14 response = session.get(url, timeout=15)
15 # 차단 감지
16 if "validateCaptcha" in response.url or "Robot Check" in response.text:
17 wait = (2 ** attempt) * 5
18 print(f"CAPTCHA가 감지되었습니다. {wait}초 대기 중...")
19 time.sleep(wait)
20 continue
21 if response.status_code in (429, 503):
22 wait = (2 ** attempt) * 2
23 print(f"요청 제한 감지({response.status_code}). {wait}초 대기 중...")
24 time.sleep(wait)
25 continue
26 if "ap_email" in response.text:
27 raise Exception("로그인 벽 — 쿠키가 만료되었습니다")
28 return BeautifulSoup(response.text, "lxml")
29 except Exception as e:
30 if attempt == max_retries - 1:
31 raise
32 print(f"시도 {attempt + 1} 실패: {e}")
33 return None
프록시 얘기도 빼놓을 수 없습니다. Amazon은 합니다. 수백 페이지 이상을 스크래핑한다면 사실상 주거용 프록시가 필요합니다. 사용량에 따라 월 $50~200 이상을 예상해야 합니다. 반면 소규모 프로젝트(하루 100 요청 이하)라면, 개인 인터넷 회선에서 조심스럽게 속도를 제한하는 것만으로도 충분한 경우가 많습니다.
Amazon은 TLS 지문도 검사합니다. Python 기본 requests 라이브러리는 WAF가 미리 차단하는 를 가지고 있습니다. 운영용 스크래퍼라면 실제 브라우저의 TLS 스택을 흉내 내는 curl_cffi를 고려해 보세요. 튜토리얼 수준(수백 페이지 정도)이라면, 헤더만 잘 맞춘 requests로도 대체로 충분합니다.
6단계: 스크래핑한 Amazon 리뷰를 CSV나 Excel로 내보내기
리뷰를 수집했다면, pandas로 바로 쓸 수 있는 형식으로 저장할 수 있습니다.
1import pandas as pd
2df = pd.DataFrame(all_reviews)
3df.to_csv("amazon_reviews.csv", index=False)
4print(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 라이브러리가 있지만, 해야 합니다. 프로젝트 생성, 두 개의 API 활성화, 서비스 계정 자격 증명 생성, 시트 공유까지 해야 하죠. 스크래핑보다 설정이 더 많게 느껴진다면, 그건 맞습니다. (처럼 한 번 클릭으로 Google Sheets에 내보내는 도구가 갑자기 엄청 매력적으로 보이는 순간이 바로 이런 때입니다.)
보너스: Python 5줄로 리뷰에 감성 분석 추가하기
대부분의 스크래핑 튜토리얼은 CSV 내보내기에서 끝납니다. 하지만 감성을 점수화해야 원시 데이터가 비즈니스 의사결정으로 바뀝니다.
가장 빠른 기준선은 TextBlob입니다.
1from textblob import TextBlob
2df["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를 추천합니다. , 를 보입니다. nlptown/bert-base-multilingual-uncased-sentiment 모델은 1~5점 별점을 직접 예측하기도 합니다.
1from transformers import pipeline
2clf = pipeline("sentiment-analysis",
3 model="nlptown/bert-base-multilingual-uncased-sentiment")
4df["predicted_stars"] = df["content"].apply(
5 lambda x: int(clf(str(x)[:512])[0]["label"][0])
6)
Amazon 리뷰는 를 따릅니다. 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 스크래퍼를 직접 유지하는 일이 너무 번거로운 상황을 위해 을 만들었습니다. 흐름은 아주 간단합니다.
- 을 설치합니다
- 브라우저에서 Amazon 상품 리뷰 페이지로 이동합니다(이미 로그인되어 있으니 로그인 벽은 문제가 되지 않습니다)
- “AI 필드 제안”을 클릭합니다 — Thunderbit이 페이지를 읽고 작성자, 평점, 제목, 리뷰 텍스트, 날짜, 인증 구매 같은 열을 제안합니다
- “스크래핑”을 클릭합니다 — 내장 페이지네이션과 함께 데이터가 바로 추출됩니다
- Excel, Google Sheets, Airtable, Notion으로 내보내기 합니다
가장 큰 장점은 Thunderbit의 AI가 매번 페이지 구조를 새로 읽는다는 점입니다. 유지보수할 CSS 셀렉터도 없고, 쿠키 관리도 없고, 봇 차단 코드를 붙잡고 씨름할 필요도 없습니다. Amazon이 HTML을 바꿔도 AI가 알아서 따라갑니다. 코드 없이 프로그래밍 방식의 접근이 필요한 사용자라면, Thunderbit의 도 있습니다. AI 기반 필드 감지로 구조화된 데이터를 API로 추출할 수 있어서 셀렉터 유지보수가 필요 없습니다.
Amazon 데이터 관련 더 깊은 내용은 과 가이드를 참고해 주세요.
Python으로 Amazon 리뷰를 대량 스크래핑할 때의 팁
여러 ASIN을 대상으로 리뷰를 수집한다면, 아래 습관이 나중에 큰 수고를 덜어줍니다.
- ASIN을 배치로 나누고 상품 사이에도 딜레이를 넣으세요(페이지 사이만 쉬는 것으로는 부족합니다). 저는 ASIN 간 10~15초 정도 멈춥니다.
- 중복 제거를 강하게 하세요. 여러 별점 필터와 정렬 조합을 합치면 겹치는 리뷰가 생깁니다.
(title, author, date)튜플을 중복 제거 키로 쓰면 좋습니다. - 실패 로그를 남기세요. 어떤 ASIN + 페이지 + 필터 조합이 실패했는지 기록해 두면, 전체를 다시 긁지 않고도 재시도할 수 있습니다.
- 대형 프로젝트는 DB에 저장하세요. CSV를 계속 키우는 것보다 간단한 SQLite 데이터베이스가 훨씬 확장성이 좋습니다.
1import sqlite3
2conn = sqlite3.connect("reviews.db")
3df.to_sql("reviews", conn, if_exists="append", index=False)
- 반복 수집을 예약하세요. 계속 모니터링해야 한다면 cron 작업을 설정하거나 Thunderbit의 Scheduled Scraper 기능을 사용하세요. URL과 일정을 설명해 두면 서버 없이 나머지를 알아서 처리합니다.
추가 방법은 와 글에서 더 확인할 수 있습니다.
법적·윤리적 고려사항에 대한 짧은 안내
Amazon의 은 Amazon 서비스에 접근하기 위해 “로봇, 스파이더, 스크래퍼 또는 기타 자동화 수단을 사용하는 행위”를 명시적으로 금지합니다. 다만 최근 미국 판례는 공개 데이터 스크래퍼에 우호적인 편이었습니다. 사건에서 연방법원은 로그인된 “사용자”가 아닌 경우, 공개적으로 접근 가능한 데이터를 스크래핑하는 것이 서비스 약관 위반이 아니라고 판시했습니다.
중요한 차이점은 로그인 뒤 데이터를 스크래핑하는 경우입니다(이 튜토리얼이 다루는 범위). 이 경우에는 계정을 만들면서 Amazon의 ToS에 동의한 셈이기 때문에 계약법 영역으로 들어가게 됩니다. 로그인 벽 뒤의 데이터를 스크래핑하는 것은 공개된 대표 리뷰만 스크래핑하는 것보다 법적 위험이 더 큽니다.
실무 지침으로는, 스크래핑한 데이터를 상업적으로 재배포하지 말 것, 공개 표시된 범위를 넘어 개인 사용자 데이터를 수집하지 말 것, robots.txt를 존중할 것, 대규모 또는 상업적 사용 시 법률 자문을 구할 것이 있습니다. 이는 법률 자문이 아닙니다. 더 자세한 법적 배경은 개요를 참고하세요.
결론: Python으로 Amazon 리뷰를 스크래핑하거나, 아예 코드를 건너뛰기
이 가이드에서 다룬 내용을 간단히 정리하면 다음과 같습니다.
- 로그인 벽은 실제로 존재하지만, 쿠키 기반 인증으로 해결할 수 있습니다. 브라우저에서 7개 쿠키를 복사해
requests.Session()에 넣으세요 - 몇 주마다 깨지는 CSS 클래스 대신
data-hook셀렉터를 사용하세요 - 별점 필터와 정렬 방식을 조합하면 10페이지 제한을 넘어 제품당 500개 이상의 리뷰에 접근할 수 있습니다
- TextBlob으로 빠른 기준선을 만들거나, Hugging Face Transformers로 운영 수준의 감성 분석을 추가하세요
- 봇 차단 대응도 챙기세요. 속도 제한, User-Agent 회전, 지수 백오프, 규모가 크면 주거용 프록시까지 필요합니다
Python은 완전한 제어를 가능하게 하고, 내부 구조를 이해하는 데 가장 좋은 방법입니다. 하지만 당신의 목표가 “운영용 데이터 파이프라인을 만들기”가 아니라 “금요일까지 경쟁사 리뷰 데이터를 스프레드시트에 넣어야 한다”라면, 커스텀 스크래퍼의 유지보수 부담은 굳이 떠안을 가치가 없을 수 있습니다.
은 인증, 셀렉터, 페이지네이션, 내보내기를 클릭 몇 번으로 처리합니다. 을 한번 써 보고 워크플로에 맞는지 확인해 보세요. Amazon이 봇 차단을 계속 강화하는 만큼, 실시간으로 적응하는 AI 기반 도구는 있으면 좋은 옵션이 아니라 점점 필수에 가까워지고 있습니다.
스크래핑 워크플로의 영상 안내가 필요하다면 도 확인해 보세요.
자주 묻는 질문
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이 유용합니다. 로그인 벽과 페이지네이션을 자동으로 처리하는 노코드 대안이 필요하다면 을 사용해 보세요.
5. Amazon 스크래핑 시 차단을 피하려면 어떻게 해야 하나요?
10개 이상의 실제 브라우저 문자열 풀에서 User-Agent를 회전시키고, 요청 사이에 2~5초의 무작위 딜레이를 넣고, 429/503 오류에는 지수 백오프를 적용하고, 규모가 크다면 주거용 프록시를 사용하세요(데이터센터 IP는 미리 차단되는 경우가 많습니다). 요청마다 세션 쿠키도 일관되게 유지해야 합니다. 유지보수 없이 가고 싶다면 Thunderbit가 브라우저 세션을 통해 봇 차단 대응을 자동으로 처리합니다.
더 알아보기
