Python으로 Walmart 데이터 수집하는 방법 (차단 없이 진행하기)

최종 업데이트: April 15, 2026

Walmart는 일부 상품의 가격을 바꿉니다. 이런 변화를 프로그램으로 따라가 본 적이 있다면 그 고생이 얼마나 큰지 바로 알 거예요. 스크립트가 20분쯤은 멀쩡히 돌다가도, 어느 순간부터는 정상적인 200 OK 응답처럼 보이는 CAPTCHA 페이지를 조용히 돌려주기 시작하거든요.

저는 에서 데이터 추출 작업을 하면서 Walmart의 봇 차단 방식을 꽤 오래 분석해 왔습니다. 그 과정에서 실제로 통하는 방법들, 2025년 기준으로 자주 놓치는 조용한 실패 지점들, 그리고 직접 스크래퍼를 만들지, 스크래핑 API를 쓸지, 아니면 노코드 도구를 바로 쓸지에 대한 현실적인 선택 기준까지 정리해 두었습니다. 이 가이드에서는 세 가지 추출 방식(HTML 파싱, __NEXT_DATA__ JSON, 내부 API 가로채기), 대부분의 튜토리얼이 아예 건너뛰는 실전용 오류 처리, 그리고 내 상황에 맞는 접근법을 고르는 판단 프레임워크까지 다룹니다. Python으로 작업하든, 점심시간 전까지 가격표가 가득한 스프레드시트를 얻고 싶든, 도움이 될 내용이 있습니다.

왜 Python으로 Walmart를 크롤링해야 할까?

Walmart는 매출 기준 세계 최대 소매업체로, FY2025 기준 를 기록했고, 를 지키고 있습니다. 사이트에는 약 의 활성 상품이 올라와 있고, Walmart CFO는 마켓플레이스에 가 있다고 언급했습니다. 이 목록의 약 가 등록한 것으로, 카탈로그가 매우 자주 바뀝니다. 판매자는 계속 교체되고, 옵션은 달라지고, 재고도 매일 뒤집힙니다.

walmart_stats_670d06c6bd.png

이런 변동성 때문에 크롤링이 중요합니다. 분기별 리포트만으로는 밤마다 수집한 데이터가 보여 주는 변화를 따라가기 어렵습니다. 대표적인 활용 사례는 다음과 같습니다.

활용 사례필요한 사람추출하는 데이터
경쟁사 가격 모니터링이커머스 운영, 재가격 책정 도구가격, 프로모션, MAP 준수 여부
상품 카탈로그 보강영업, 머머다이징 팀설명, 이미지, 사양, 옵션
재고 가용성 추적공급망, 드롭쉬퍼재고 상태, 판매자 정보
시장 조사 및 트렌드 분석마케팅, 제품 관리자평점, 리뷰, 카테고리 구성
리드 발굴영업팀판매자 이름, 상품 수, 카테고리

경쟁사 가격 모니터링 소프트웨어 시장만 해도 규모에 도달했고, 2033년에는 50억 9천만 달러까지 성장할 전망입니다. 이런 수요는 소비자 행동에서 나옵니다. 하고, 83%는 여러 사이트를 오가며 가격을 비교합니다.

Python은 이 작업의 기본 언어입니다. Apify의 2026 Infrastructure Report에 따르면 으로 이뤄지고 있으며, 핵심 라이브러리인 requests를 기록합니다. 어느 정도 규모 이상으로 스크래핑한다면, Python을 쓰고 있을 가능성이 매우 높습니다.

Walmart가 특히 크롤링하기 어려운 이유

Walmart는 두 개의 상용 안티봇 제품을 연속으로 사용하기 때문에 특히 까다롭습니다. 엣지 WAF와 TLS 핑거프린팅 계층에는 가, 행동 기반 JavaScript 챌린지 계층에는 가 들어갑니다. Scrape.do는 이 조합을 "드물고, 우회하기 극도로 어렵다"고 표현합니다.

walmart_antibot_3d67d0119c.png

으로 평가하며, Akamai 단독으로도 9점입니다. 제 경험상 이 평가는 꽤 정확합니다.

실제로 상대해야 하는 것은 다음과 같습니다.

Akamai Bot Manager는 TLS 지문(JA3/JA4 해시), HTTP/2 프레임 순서, 헤더의 순서와 대소문자, 세션 쿠키(_abck, ak_bmsc)를 검사합니다. 일반적인 Python requests 호출은 실제 브라우저가 절대 만들지 않는 TLS 지문을 내보내기 때문에, 요청이 Walmart 서버에 도달하기도 전에 Akamai가 차단합니다.

PerimeterX/HUMAN은 Akamai 다음 단계에서 동작하며, px.js 기반 JavaScript 지문 채집으로 navigator 속성, canvas 렌더링, WebGL, audio context, 그리고 행동 생체 신호(마우스 움직임, 스크롤 속도, 키 입력 패턴)를 확인합니다. 사용자가 화면에서 직접 겪는 대표적인 실패가 바로 입니다. 약 10초 동안 버튼을 누르고 있어야 하며, 그동안 행동 신호가 샘플링됩니다. Oxylabs도 단호하게 말합니다. "Walmart는 PerimeterX가 제공하는 'Press & Hold' CAPTCHA 방식을 사용하며, 이는 코드만으로 해결하기 거의 불가능하다."

진짜 위험한 건 조용한 차단(silent block) 입니다. Walmart는 403 대신 CAPTCHA 본문이 포함된 HTTP 200을 반환합니다. 에 따르면, "Walmart는 CAPTCHA 페이지를 제공할 때도 200 OK 상태 코드를 반환한다. 상태 코드만으로 요청 성공 여부를 판단할 수 없다." 스크립트는 CAPTCHA HTML을 "상품이 없음"으로 오해한 채 계속 진행하고, 데이터셋 절반이 쓰레기가 되는데도 그 사실을 모르게 됩니다.

여기에 매장별 데이터 문제도 있습니다. Walmart의 가격과 재고는 지역별로 다르며, locDataV3, assortmentStoreId 같은 쿠키로 제어됩니다. 올바른 쿠키가 없으면 "기본 전국 데이터"가 내려오는데, 보기에는 완전해 보여도 실제 고객이 보는 값과 다를 수 있습니다. 쿠키 누락은 차단 페이지를 만들지 않습니다. 대신 겉으로는 멀쩡하지만 틀린 데이터를 줍니다. 이건 더 나쁩니다.

Walmart에서 데이터를 추출하는 세 가지 방법과 비교

실제 단계별 설명에 들어가기 전에, 핵심 추출 방식 세 가지를 먼저 보겠습니다. 경쟁사 튜토리얼은 보통 하나나 둘만 다루지만, 여기서는 상황에 맞게 고를 수 있도록 세 가지를 모두 설명합니다.

방법안정성데이터 완성도안티봇 난도유지보수 부담
HTML + BeautifulSoup⚠️ 낮음(배포 때마다 셀렉터가 깨짐)보통높음높음
__NEXT_DATA__ JSON✅ 좋음높음중~높음중간
내부 API 가로채기✅ 최상최고(옵션, 재고, 리뷰 포함)중~높음낮음(구조화된 JSON)
Thunderbit (노코드)✅ 좋음높음낮음(AI가 처리)없음

Walmart에서는 HTML 파싱이 가장 좋지 않은 선택입니다. 이 사이트는 배포할 때마다 바뀌는 해시 처리된 CSS 클래스 이름을 가진 Next.js 번들을 사용합니다. __NEXT_DATA__ JSON 방식은 2024~2026년의 진지한 오픈소스 Walmart 스크래퍼라면 대부분 채택하는 현실적인 선택입니다. 내부 API 가로채기는 가장 강력하지만, 대부분의 글이 대충 넘어가는 주의점이 있습니다. 그리고 아예 커스텀 파이프라인이 필요 없다면 Thunderbit이 맞는 선택입니다.

Python 환경 설정하기: Walmart 크롤링 준비

준비물은 다음과 같습니다.

  • 난이도: 중급
  • 소요 시간: 설정 약 30분 + 코딩 시간
  • 준비물: Python 3.10+, pip, 코드 에디터, 그리고 실전용이라면 프록시 서비스 또는 스크래핑 API

프로젝트 폴더와 가상환경을 만듭니다.

1mkdir walmart-scraper && cd walmart-scraper
2python -m venv venv
3source venv/bin/activate  # Windows에서는: venv\Scripts\activate

필요한 라이브러리를 설치합니다.

1pip install curl_cffi parsel beautifulsoup4 lxml

curl_cffi는 2025년 기준으로 어려운 사이트를 크롤링할 때 사실상 표준입니다. 정확한 브라우저 TLS 지문을 흉내 낼 수 있는 libcurl 바인딩입니다. 에 따르면, "Walmart는 봇 탐지의 일부로 TLS 지문을 사용하며, User-Agent만 진짜 브라우저처럼 바꿔도 우회할 수 없다." 일반 requestshttpx는 어떤 헤더를 넣어도 Akamai를 통과할 수 없습니다. impersonate="chrome124"를 설정한 curl_cffi가 차이를 만듭니다.

또한 나중에 다룰 실전 패턴을 위해 json(내장), csv(내장), time, random, logging도 함께 있으면 좋습니다.

단계별: Python으로 Walmart 상품 페이지 크롤링하기

1단계: Walmart 상품 페이지 가져오기

먼저 해야 할 일은 바로 차단되지 않는 HTTP 요청을 보내는 것입니다. 아래는 2024~2026년 Scrapfly, Scrapingdog, Oxylabs, ScrapeOps 전반에서 공통적으로 쓰이는 표준 헤더 세트입니다.

1from curl_cffi import requests
2HEADERS = {
3    "User-Agent": (
4        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
5        "AppleWebKit/537.36 (KHTML, like Gecko) "
6        "Chrome/124.0.0.0 Safari/537.36"
7    ),
8    "Accept": (
9        "text/html,application/xhtml+xml,application/xml;q=0.9,"
10        "image/avif,image/webp,*/*;q=0.8"
11    ),
12    "Accept-Language": "en-US,en;q=0.9",
13    "Accept-Encoding": "gzip, deflate, br",
14    "Upgrade-Insecure-Requests": "1",
15    "Sec-Fetch-Dest": "document",
16    "Sec-Fetch-Mode": "navigate",
17    "Sec-Fetch-Site": "none",
18    "Sec-Fetch-User": "?1",
19    "Referer": "https://www.google.com/",
20}
21session = requests.Session(impersonate="chrome124")
22url = "https://www.walmart.com/ip/Apple-AirPods-Pro-2nd-Generation/1752657021"
23response = session.get(url, headers=HEADERS)

여기서 핵심은 impersonate="chrome124"입니다. 이 설정은 curl_cffi가 Chrome 124의 정확한 TLS ClientHello, HTTP/2 프레임 순서, pseudo-header 순서를 맞추게 합니다. 이게 없으면 Akamai는 Python 특유의 JA3 해시를 보고, 요청이 Walmart 애플리케이션 계층에 닿기도 전에 막아버립니다.

차단된 응답의 특징: HTML 제목에 "Robot or human?"가 보이거나, 응답이 walmart.com/blocked로 리다이렉트된다면 이미 차단된 것입니다. 문제는 Walmart가 종종 CAPTCHA 본문과 함께 200 상태 코드를 반환한다는 점입니다. 따라서 response.ok만 확인해서는 안 됩니다.

실서비스나 반복 수집을 하려면 주거용 프록시(residential proxy) 가 필요합니다. 데이터센터 IP는 Akamai의 IP 평판 시스템에 바로 걸립니다. 아래 실전용 오류 처리와 프록시 전략에서 자세히 설명하겠습니다.

2단계: __NEXT_DATA__ JSON에서 상품 데이터 파싱하기

Walmart.com은 Next.js 애플리케이션이며, 서버 렌더링 HTML 안에 전체 하이드레이션 페이로드를 <script id="__NEXT_DATA__" type="application/json"> 하나에 넣습니다. 이게 바로 핵심입니다.

에 따르면, "2026년 기준 Walmart는 __NEXT_DATA__ 스크립트 태그 안에 구조화된 JSON을 사용하므로, 전통적인 CSS 셀렉터 파싱보다 숨겨진 데이터 추출이 훨씬 안정적이다." , , 같은 유명 오픈소스 Walmart 스크래퍼들도 모두 이 방식을 사용합니다.

추출 방법은 다음과 같습니다.

1import json
2from parsel import Selector
3sel = Selector(text=response.text)
4raw = sel.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
5data = json.loads(raw)
6product = data["props"]["pageProps"]["initialData"]["data"]["product"]
7idml = data["props"]["pageProps"]["initialData"]["data"].get("idml", {})

대부분의 튜토리얼은 여기서 끝납니다. 하지만 아래는 실제로 필요한 필드를 기준으로 정리한 완전한 JSON 경로 맵입니다. 2024~2026년의 실제 Walmart 페이지에서 검증한 내용입니다.

데이터 필드JSON 경로(initialData 기준)타입비고
상품명data > product > nameString
브랜드data > product > brandString
현재 가격(숫자)data > product > priceInfo > currentPrice > priceFloat매장 쿠키에 따라 달라질 수 있음
현재 가격(문자열)data > product > priceInfo > currentPrice > priceStringString예: "$9.99" 형태
짧은 설명data > product > shortDescriptionHTML String텍스트로 쓰려면 BeautifulSoup으로 파싱
긴 설명data > idml > longDescriptionHTML Stringproduct 안이 아니라 idml에 있음 — 예전 튜토리얼이 자주 틀리는 부분
전체 이미지data > product > imageInfo > allImagesArray{id, url} 객체 목록
평균 평점data > product > averageRatingFloat레거시 rating이 아니라 averageRating
리뷰 수data > product > numberOfReviewsInteger
옵션data > product > variantCriteriaArray사이즈, 색상 같은 옵션 그룹
재고 상태data > product > availabilityStatusStringIN_STOCK, OUT_OF_STOCK, LIMITED_STOCK
판매자data > product > sellerDisplayNameString
제조사data > product > manufacturerNameString

longDescription 경로는 사람들이 자주 놓치는 함정입니다. 2023년 ScrapeHero 글은 이를 product.longDescription에 있다고 했지만, 2024년 이후 자료들은 일관되게 형제 키인 idml에 둡니다. 항상 먼저 idml.longDescription을 읽고, 구버전 페이지에 대비해 product.longDescription을 fallback으로 두세요.

.get() 체인을 쓰는 안전한 추출 패턴은 다음과 같습니다.

1def extract_product(data):
2    product = data["props"]["pageProps"]["initialData"]["data"]["product"]
3    idml = data["props"]["pageProps"]["initialData"]["data"].get("idml", {})
4    price_info = product.get("priceInfo", {})
5    current_price = price_info.get("currentPrice", {})
6    image_info = product.get("imageInfo", {})
7    return {
8        "name": product.get("name"),
9        "brand": product.get("brand"),
10        "price": current_price.get("price"),
11        "price_string": current_price.get("priceString"),
12        "short_desc": product.get("shortDescription"),
13        "long_desc": idml.get("longDescription", product.get("longDescription")),
14        "images": [img.get("url") for img in image_info.get("allImages", [])],
15        "rating": product.get("averageRating"),
16        "review_count": product.get("numberOfReviews"),
17        "variants": product.get("variantCriteria"),
18        "availability": product.get("availabilityStatus"),
19        "seller": product.get("sellerDisplayName"),
20        "manufacturer": product.get("manufacturerName"),
21    }

JSON 경로를 직접 다루고 싶지 않은 사용자라면, 가 이런 필드를 자동으로 찾아 구조화해 줍니다. 수동으로 경로를 매핑할 필요가 없습니다. "AI Suggest Fields"를 누르면 페이지를 읽고 표를 만들어 줍니다. 하지만 직접 파이프라인을 구축한다면, 위의 맵이 기준이 됩니다.

3단계: Walmart 내부 API 엔드포인트를 가로채 더 풍부한 데이터 얻기

이 방법을 제대로 설명한 경쟁사 글은 거의 없습니다. 가장 강력한 추출 방식이지만, 동시에 가장 복잡합니다.

Walmart의 프론트엔드는 를 호출합니다. 엔드포인트는 www.walmart.com/orchestra/* 아래에 있습니다.

  • /orchestra/pdp/graphql/... — 상품 상세 하이드레이션 + 옵션 전환
  • /orchestra/snb/graphql/... — 검색 및 탐색 페이지네이션
  • /orchestra/reviews/graphql/... — 페이지네이션 리뷰

이 엔드포인트들은 __NEXT_DATA__에서 종종 잘려 나가는 데이터까지 포함한 깔끔하고 구조화된 JSON을 반환합니다. 옵션별 가격, 실시간 재고 수량, 전체 리뷰 페이지네이션 같은 정보가 여기에 있습니다.

대부분의 블로그가 얼버무리는 핵심 문제: Walmart는 를 사용합니다. 요청 본문에는 쿼리 텍스트가 아니라 SHA-256 해시(persistedQuery.sha256Hash)만 들어갑니다. 이 해시를 서버가 모르면 PersistedQueryNotFound가 뜹니다. Walmart는 배포 때마다 이 해시를 바꿉니다. 그래서 유명 오픈소스 Walmart 스크래퍼들 중 어느 것도 /orchestra/ 코드를 그대로 복붙할 수 있게 공개하지 않는 겁니다.

이 방법을 현실적으로 쓰려면 DevTools에서 직접 확인해야 합니다.

  1. Chrome에서 Walmart 상품 페이지를 연다
  2. DevTools → Network 탭을 열고 "Fetch/XHR"로 필터링한다
  3. 페이지를 평소처럼 탐색한다 — 옵션을 누르고, 리뷰를 스크롤하고, 매장 위치를 바꿔 본다
  4. 상품 데이터가 들어 있는 /orchestra/* 요청을 찾는다
  5. 요청을 우클릭 → "Copy as cURL"
  6. 그 cURL을 curl_cffi로 Python에 옮긴다

재생한 API 호출은 이런 형태입니다.

1import json
2from curl_cffi import requests
3session = requests.Session(impersonate="chrome124")
4# 먼저 상품 페이지를 방문해서 세션을 워밍업한다
5session.get("https://www.walmart.com/ip/some-product/1234567", headers=HEADERS)
6# 그다음 내부 API 호출을 재생한다(DevTools에서 복사)
7api_url = "https://www.walmart.com/orchestra/pdp/graphql"
8api_headers = {
9    **HEADERS,
10    "accept": "application/json",
11    "content-type": "application/json",
12    "referer": "https://www.walmart.com/ip/some-product/1234567",
13    "wm_qos.correlation_id": "복사한-correlation-id",
14}
15payload = {
16    # DevTools에서 복사한 정확한 요청 본문을 넣는다
17    "variables": {"productId": "1234567"},
18    "extensions": {
19        "persistedQuery": {
20            "version": 1,
21            "sha256Hash": "복사한-hash"
22        }
23    }
24}
25api_response = session.post(api_url, headers=api_headers, json=payload)
26api_data = api_response.json()

세션 워밍업 단계는 매우 중요합니다. Walmart의 PerimeterX 쿠키(_px3, _pxhd, ACID)는 API 호출이 성공하기 전에 초기 HTML 요청으로 먼저 설정되어야 합니다. 그렇지 않으면 412나 403이 나옵니다.

이 방법을 쓸 때: __NEXT_DATA__에 없는 데이터가 필요할 때입니다. 세부 옵션 가격, 첫 배치 이후의 리뷰 페이지네이션, 실시간 재고 수량 등이 여기에 해당합니다. 대부분의 경우에는 __NEXT_DATA__만으로 충분하고 훨씬 단순합니다.

Walmart 검색 결과와 여러 페이지를 크롤링하기

검색 결과도 비슷하게 __NEXT_DATA__ 패턴을 따르지만, JSON 경로는 다릅니다.

1search_url = "https://www.walmart.com/search?q=laptops&page=1"
2response = session.get(search_url, headers=HEADERS)
3sel = Selector(text=response.text)
4raw = sel.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
5data = json.loads(raw)
6search_result = data["props"]["pageProps"]["initialData"]["searchResult"]
7items = search_result["itemStacks"][0]["items"]
8# 광고 상품 제외
9organic_items = [i for i in items if i.get("__typename") == "Product"]
10for item in organic_items:
11    print(item.get("name"), item.get("priceInfo", {}).get("currentPrice", {}).get("price"))

페이지네이션은 page 파라미터를 늘리면 됩니다: &page=1, &page=2 식입니다. 하지만 문서화되지 않은 제한이 하나 있습니다. Walmart는 총 결과 수와 상관없이 검색 결과를 25페이지까지만 보여 줍니다. . "Walmart는 실제 총 페이지 수와 무관하게 접근 가능한 결과 페이지 수를 최대 25페이지로 제한한다."고 합니다.

더 깊게 커버하려면 다음 같은 우회법을 씁니다.

  • 정렬 조건 바꾸기: 같은 검색어에 &sort=price_low, &sort=price_high를 번갈아 적용하면 약 50페이지 정도를 확보할 수 있습니다.
  • 가격 범위 분할: &min_price=X&max_price=Y를 붙여 카탈로그를 작은 구간으로 나눕니다.
  • 카테고리 단위 분할: 전체 사이트가 아니라 특정 카테고리 안에서 검색합니다.

itemStacks는 배열입니다. Scrapfly는 저장소에서 [0]만 고정으로 쓰지만, 카테고리와 탐색 페이지에는 종종 여러 스택("Top picks", "More results")이 존재합니다. 더 견고한 방식은 모든 스택을 순회하는 것입니다.

1for stack in search_result.get("itemStacks", []):
2    for item in stack.get("items", []):
3        if item.get("__typename") == "Product":
4            # item 처리
5            pass

또 하나 알아둘 점은, Walmart의 robots.txt한다는 것입니다. 제품 상세 페이지(/ip/...)와 대부분의 카테고리 페이지(/cp/...)는 금지되어 있지 않습니다. 준수 측면이 걱정된다면, 검색 페이지보다 제품 페이지와 카테고리 트리부터 시작하는 편이 낫습니다.

조용한 차단이 데이터를 망치지 않게 하려면: 실전용 오류 처리

대부분의 튜토리얼은 여기서 무너집니다. 한 페이지를 가져오고, 한 상품을 파싱하고, 끝이라고 말하죠. 하지만 실제 운영에서는 수천 페이지를 긁어야 하고, Walmart는 적극적으로 이를 막으려 합니다. 데모용 스크래퍼와 실제로 돌아가는 스크래퍼의 차이는 실패를 어떻게 다루는가에 있습니다.

데이터가 망가지기 전에 조용한 차단 감지하기

Walmart 스크래퍼에서 가장 중요한 함수는 차단 감지기입니다. , , , 같은 업체들의 공통된 조언을 종합하면, 독립적인 네 가지 확인이 필요합니다.

1BLOCK_MARKERS = (
2    "Robot or human",
3    "Press &amp; Hold",
4    "Press & Hold",
5    "px-captcha",
6    "perimeterx",
7)
8def is_walmart_blocked(response) -> bool:
9    # 1. 전용 차단 엔드포인트로 리다이렉트되었는지
10    if "/blocked" in str(response.url):
11        return True
12    # 2. 명확한 상태 코드
13    if response.status_code in (403, 412, 428, 429, 503):
14        return True
15    # 3. 200 OK인데 CAPTCHA 본문인 경우(조용한 차단)
16    body = response.text or ""
17    if any(m.lower() in body.lower() for m in BLOCK_MARKERS):
18        return True
19    # 4. 응답 길이 확인 — 진짜 PDP는 보통 300~900KB
20    if len(response.content) &lt; 50_000 and "/ip/" in str(response.url):
21        return True
22    return False

네 번째 체크인 응답 길이는, 눈에 띄는 CAPTCHA 문구는 없지만 상품 데이터도 없는 경우를 잡아냅니다.

지수 백오프와 지터를 이용한 재시도 로직

요청이 실패했다고 바로 Walmart를 계속 두드리면 안 됩니다. 일반적인 패턴은 재시도를 분산시키기 위해 지터가 포함된 지수 백오프를 쓰는 것입니다.

1import time
2import random
3import logging
4from curl_cffi import requests as cffi_requests
5log = logging.getLogger("walmart")
6def fetch_with_retry(session, url, max_retries=5, base_delay=2, max_delay=60):
7    for attempt in range(max_retries):
8        try:
9            response = session.get(url, headers=HEADERS, timeout=15)
10            if response.status_code in (429, 503):
11                raise Exception(f"Throttled: {response.status_code}")
12            if is_walmart_blocked(response):
13                raise Exception("Silent block detected")
14            return response
15        except Exception as e:
16            if attempt == max_retries - 1:
17                raise
18            wait = min(max_delay, base_delay * (2 ** attempt)) + random.uniform(0, 3)
19            log.warning(f"시도 {attempt + 1} 실패: {e}. {wait:.1f}초 후 재시도합니다")
20            time.sleep(wait)
21    return None

지터(random.uniform(0, 3))는 장식이 아닙니다. 여러 워커가 같은 초에 동시에 재시도해 Akamai의 속도 탐지에 걸리지 않도록 분산시켜 줍니다.

속도 제한

모두 Walmart에 대해 요청마다 3~6초 정도의 랜덤 지연을 권장합니다. "페이지를 불러올 때마다 3~6초씩 기다리고, 지연 시간을 랜덤화하라"는 조언입니다.

1import time
2import random
3def rate_limited_fetch(session, url):
4    response = fetch_with_retry(session, url)
5    time.sleep(random.uniform(3.0, 6.0))
6    return response

대규모에서는 비동기 속도 제한을 위해 aiolimiter를 고려할 수 있습니다.

1from aiolimiter import AsyncLimiter
2limiter = AsyncLimiter(max_rate=10, time_period=60)  # 분당 10회 요청

데이터 검증

응답이 차단되지 않았더라도, 파싱된 데이터가 틀렸을 수 있습니다(잘못된 매장, 축소된 페이로드 등). 출력하기 전에 검증하세요.

1def validate_product(product):
2    """상품 데이터가 정상적으로 보이면 True를 반환한다."""
3    if not product.get("name"):
4        return False
5    price = (product.get("priceInfo") or {}).get("currentPrice", {}).get("price")
6    if not isinstance(price, (int, float)) or price &lt;= 0:
7        return False
8    if product.get("availabilityStatus") not in ("IN_STOCK", "OUT_OF_STOCK", "LIMITED_STOCK"):
9        return False
10    return True

세션 로깅

세션별 성공률을 추적하세요. 10분 동안 성공률이 80% 아래로 떨어지면 뭔가 바뀐 것입니다. IP가 소진됐거나, 쿠키가 만료됐거나, Walmart가 새 안티봇 규칙을 배포했을 가능성이 큽니다.

1class ScrapeMetrics:
2    def __init__(self):
3        self.total = 0
4        self.success = 0
5        self.blocks = 0
6        self.errors = 0
7    def record(self, result):
8        self.total += 1
9        if result == "success":
10            self.success += 1
11        elif result == "blocked":
12            self.blocks += 1
13        else:
14            self.errors += 1
15    @property
16    def success_rate(self):
17        return (self.success / self.total * 100) if self.total &gt; 0 else 0
18    def check_health(self):
19        if self.total &gt; 20 and self.success_rate &lt; 80:
20            log.critical(f"성공률이 {self.success_rate:.1f}%로 떨어졌습니다 — 프록시를 교체하거나 잠시 중단하는 것을 고려하세요")

화려하진 않지만, 데이터를 깨끗하게 유지하는 핵심입니다.

직접 Python으로 만들기 vs. 스크래핑 API vs. 노코드: Walmart 수집 방법 고르기

많은 개발자들이 이게 맞는지 먼저 따져 보지 않고 바로 커스텀 스크래퍼를 만듭니다. 하지만 합니다. 포럼 사용자들도 "사실상 9/10"이라고 말하며, "전용 웹 스크래핑 API가 과한지" 고민합니다. 답은 데이터량, 예산, 그리고 엔지니어링 역량에 달려 있습니다.

요소직접 Python(requests + 프록시)스크래핑 API(Oxylabs, Bright Data 등)노코드 도구(Thunderbit)
첫 결과까지 걸리는 시간수 시간15~60분약 2분
운영 단계까지의 시간40~80시간4~16시간약 30분
안티봇 처리직접 관리(어려움)제공업체가 처리자동 처리
소규모 비용(<월 1천 페이지)낮음(프록시 비용 약 $4~8/GB)월 $40~$49부터무료~$15/월
대규모 비용(월 10만 페이지 이상)요청당 비용 낮음요청당 비용 높음변동 있음
커스터마이징완전 제어API 파라미터 수준UI/필드 제약 있음
유지보수월 4~8시간거의 없음없음(AI가 적응)
적합한 사용자맞춤 파이프라인을 만드는 개발자중간 규모 운영 스크래핑비즈니스 사용자, 빠른 일회성 추출

직접 Python이 적합한 경우

이미 프록시 계약이 있거나, 헤더, 우편번호 타게팅, 판매자 그룹까지 엄격하게 제어해야 하거나, 월 수백만 페이지처럼 API 단가가 누적되는 규모이거나, 온프레미스나 규정 준수 보장이 필요한 경우에는 직접 구현이 유리합니다. 대신 실제 공수가 듭니다. 페이지네이션, 재시도, 프록시 교체, TLS 모사, 여러 페이지 유형의 스키마를 포함한 운영용 Scrapy 스파이더를 만들려면 이 들고, Walmart가 지문을 바꿀 때마다 월 4~8시간 정도의 유지보수가 필요합니다.

스크래핑 API가 시간을 아껴주는 경우

스크래핑 API는 안티봇 계층을 대신 처리해 주므로, 사용자가 직접 신경 쓰지 않아도 됩니다. 에 따르면 Walmart에서 성공률, Scrape.do는 98% 성공률을 보입니다. , , 같은 도구는 입문 요금이 월 $40~$49 수준입니다. 개발자 2~5명 규모 팀이고, 월 1만~100만 페이지 정도를 수집한다면 API가 거의 항상 맞는 선택입니다. 요청당 비용을 조금 지불하는 대신 유지보수 부담이 사실상 사라집니다.

노코드가 맞는 경우

는 완전히 다른 유형에 맞습니다. 이번 오후 안에 Walmart 상품 데이터를 스프레드시트로 받아야 하는 PM, 분석가, 이커머스 운영자라면, 노코드 도구가 가장 솔직한 답입니다.

사용 방법은 간단합니다. 을 설치한 뒤 Walmart 상품 페이지나 검색 페이지로 이동하고, "AI Suggest Fields"를 누르면 Thunderbit의 AI가 페이지를 읽고 열(상품명, 가격, 평점 등)을 제안합니다. "Scrape"를 누르면 데이터가 표로 채워집니다. Excel, Google Sheets, Airtable, Notion으로 내보낼 수 있고, 모두 무료입니다.

Thunderbit은 클라우드에서 안티봇을 처리하므로 CAPTCHA, 프록시, TLS 핑거프린팅을 직접 다룰 필요가 없습니다. AI가 레이아웃 변경에도 자동으로 적응하므로 유지보수도 없습니다. JSON 경로를 직접 다루고 싶지 않은 사용자에게는 이보다 부담이 적은 선택지가 없습니다.

솔직한 한계도 있습니다. Thunderbit은 하루 10만 페이지 이상을 상정한 도구는 아닙니다. 크레딧 예산과 클라우드 상한 때문에 고볼륨 수집은 순수 API보다 비경제적일 수 있습니다. 또한 도구가 지원하지 않으면 특정 우편번호나 ASN을 고정할 수도 없습니다. 지속적이고 대용량인 파이프라인이라면 여전히 직접 구현이나 스크래핑 API가 더 적합합니다.

대략적인 가격 감각: Thunderbit에서 Walmart 상품 1,000행을 수집하는 데는 약 2,000 크레딧이 들며, Starter/Pro 기준 대략 $0.60~$1.10 정도입니다. 저볼륨 기준으로는 Oxylabs의 Walmart API와 비슷하고, 대부분의 취미용 스크래핑 API보다 저렴합니다. 최신 정보는 를 확인하세요.

수집한 Walmart 데이터 내보내기

데이터를 얻었다면, 이제 쓸 수 있는 형태로 옮겨야 합니다. 대부분의 요구사항은 세 가지 포맷으로 커버됩니다.

CSV — 분석가들이 실제로 여는 가장 기본적인 형식입니다.

1import csv
2def export_csv(products, filename="walmart_products.csv"):
3    fieldnames = ["name", "price", "availability", "rating", "review_count", "seller", "url"]
4    with open(filename, "w", newline="", encoding="utf-8-sig") as f:
5        writer = csv.DictWriter(f, fieldnames=fieldnames, quoting=csv.QUOTE_MINIMAL)
6        writer.writeheader()
7        for p in products:
8            writer.writerow({k: p.get(k) for k in fieldnames})

Excel 호환성을 위해 utf-8-sig 인코딩을 쓰세요. BOM 마커가 특수문자 깨짐을 막아 줍니다.

JSONL — 스크래핑 파이프라인의 실전 형식입니다.

1import json
2import gzip
3def export_jsonl(products, filename="walmart_products.jsonl.gz"):
4    with gzip.open(filename, "at", encoding="utf-8") as f:
5        for p in products:
6            f.write(json.dumps(p, ensure_ascii=False) + "\n")

중간에 쓰기가 끊겨도 마지막 줄만 잃으며, 일정한 메모리로 스트리밍할 수 있고, 옵션과 리뷰 같은 중첩 데이터도 그대로 유지합니다.

Excel — 한 번 넘겨주기 위한 분석 작업에 적합합니다.

1from openpyxl import Workbook
2def export_excel(products, filename="walmart_products.xlsx"):
3    wb = Workbook(write_only=True)
4    ws = wb.create_sheet("Products")
5    ws.append(["Name", "Price", "Availability", "Rating", "Reviews", "Seller"])
6    for p in products:
7        ws.append([p.get("name"), p.get("price"), p.get("availability"),
8                    p.get("rating"), p.get("review_count"), p.get("seller")])
9    wb.save(filename)

Thunderbit은 Python을 쓰지 않는 사용자에게도 내보내기 문제를 해결해 줍니다. Google Sheets, Airtable, Notion, Excel, CSV, JSON으로 한 번에 내보낼 수 있고, 기본 요금제에서도 모두 무료입니다. 지속적인 모니터링이 필요하다면 Thunderbit의 예약 스크래퍼 기능으로 반복 수집을 자동 실행할 수 있습니다.

일정 예약에 대한 한 가지 주의점: . GitHub Actions 러너는 Walmart의 안티봇이 즉시 막는 Azure IP 대역에 있습니다. VPS에서 APScheduler를 쓰거나, 모든 트래픽을 주거용 프록시로 보내세요.

Walmart 크롤링의 법적·윤리적 가이드라인

포럼 사용자들도 이런 우려를 직접 표현합니다. "개발자와 술래잡기하는 건 괜찮지만, 법무팀과 놀고 싶진 않다"는 식이죠.

Walmart 이용약관합니다. 사전 서면 동의 없이 "robot, spider… 또는 기타 수동/자동 장치로 자료를 가져오거나, 색인화하거나, 'scrape'하거나, 'data mine'하거나, 그 밖에 수집하는 행위"는 허용되지 않습니다.

Walmart robots.txt합니다. 제품 상세 페이지(/ip/...)와 리뷰(/reviews/product/)는 금지 대상이 아닙니다.

hiQ v. LinkedIn 판례(9th Circuit, )는 공개적으로 접근 가능한 데이터를 스크래핑하는 행위가 연방법 CFAA를 위반할 가능성은 낮다고 봤습니다. 하지만 같은 사건에서 법원은 고 판단했고, 을 내렸습니다. 2024년의 더 최근 판결들(, )은 CFAA 적용 범위를 더 좁히고 저작권 선점 방어 논리를 만들었지만, 이 판결들은 Walmart에 그대로 맞아떨어지는 약관 구조를 전제로 한 것은 아닙니다.

실무 지침: 서버를 과도하게 두드리지 마세요. 속도 제한을 지키세요. 개인 정보나 사용자 데이터를 수집하지 마세요. 공개된 Walmart 상품 페이지를 적당한 속도로 개인 연구용으로 수집하는 것과, 상업적 규모로 Walmart 약관에 반해 수집하는 것은 위험도가 다릅니다. Walmart 데이터를 기반으로 제품을 만들고 있다면 변호사와 상의하고, Walmart의 공식 도 검토하세요.

면책 조항: 본 내용은 교육 목적이며, 법률 자문이 아닙니다.

결론과 핵심 정리

Walmart를 Python으로 크롤링하는 일은 듀얼 Akamai + PerimeterX 안티봇 스택 때문에 급 과제입니다. 불가능한 건 아니지만, 적절한 도구와 패턴이 필요합니다.

핵심 요약:

  • 대부분의 경우 __NEXT_DATA__ JSON 추출이 가장 현실적입니다. 2024~2026년의 진지한 오픈소스 Walmart 스크래퍼는 모두 이 방식을 사용합니다. PDP의 기본 경로는 props.pageProps.initialData.data.product, 검색/탐색은 searchResult.itemStacks입니다.
  • impersonate="chrome124"를 설정한 curl_cffi는 필수입니다. 일반 requestshttpx는 헤더를 바꿔도 Akamai의 TLS 핑거프린팅을 통과할 수 없습니다.
  • 진짜 위험은 조용한 차단입니다. Walmart는 CAPTCHA 본문과 함께 200 OK를 반환합니다. 상태 코드만 보지 말고 응답 본문을 확인하세요.
  • 실전 스크래퍼는 정상 경로 코드만으로는 부족합니다. 지터가 포함된 지수 백오프, 네 가지 신호를 이용한 차단 감지, 요청당 3~6초 수준의 속도 제한, 데이터 검증, 세션 상태 모니터링이 모두 필요합니다.
  • /orchestra/*를 통한 내부 API 가로채기는 강력하지만 취약합니다. 주요 추출 방식이 아니라, 특정 데이터가 필요할 때 DevTools 실험용으로 쓰세요.
  • Walmart 검색 결과는 25페이지로 제한됩니다. 정렬 방식 전환과 가격 범위 분할로 더 넓게 커버할 수 있습니다.
  • 접근 방식은 솔직하게 고르세요: 맞춤 요구와 대용량이 있는 개발자라면 직접 Python. 전담 스크래핑 엔지니어 없이 중간 규모로 운영하려면 스크래핑 API. 이번 오후 안에 Google Sheets로 데이터를 받고 싶은 비즈니스 사용자라면 .

노코드 경로를 시험해 보고 싶다면, 에는 무료 플랜이 있습니다. 몇 개의 Walmart 페이지를 직접 긁어 결과를 확인해 보세요. Python 경로를 택한다면, 이 글의 코드 패턴은 실전 검증을 거친 것들입니다. 어느 쪽이든 이제 Walmart의 방어 체계와 그걸 통과하는 세 가지 경로를 알게 되었습니다.

웹 스크래핑 기법에 대해 더 알고 싶다면 , , 가이드를 확인해 보세요. 에서도 튜토리얼을 볼 수 있습니다.

FAQ

Walmart 상품 데이터를 크롤링하는 것은 합법인가요?

Walmart의 이용약관은 서면 동의 없는 자동 스크래핑을 금지합니다. 9th Circuit의 hiQ v. LinkedIn 판결(2022)은 공개 페이지 스크래핑에 연방법 CFAA가 적용되기 어렵다는 점을 보여 줬지만, 같은 사건은 결국 스크래퍼에게 로 끝났습니다. 개인 연구 목적으로 공개 상품 페이지를 적당한 속도로 수집하는 것과, 상업적 규모로 수집하는 것은 위험도가 다릅니다. Walmart 데이터를 기반으로 사업을 하려면 변호사와 상담하세요.

왜 제 Walmart 스크래퍼는 계속 차단될까요?

가장 흔한 원인은 다음과 같습니다. 일반 requestshttpx 사용(Python 특유의 TLS 지문이 Akamai에 즉시 걸림), 잘못되었거나 부족한 헤더, 프록시 교체 없음, 페이지당 3~6초보다 빠른 요청 속도, 세션 쿠키(_px3, _abck, locDataV3) 누락입니다. impersonate="chrome124"가 설정된 curl_cffi로 전환하고, 주거용 프록시를 사용하고, 이 글에서 설명한 차단 감지와 재시도 패턴을 구현하세요.

Python으로 Walmart에서 어떤 데이터를 수집할 수 있나요?

상품명, 가격(현재가 및 할인 전 가격), 이미지, 짧은/긴 설명, 평점, 리뷰 수, 재고 상태, 판매자 이름, 제조사 정보, 옵션(size, color), 카테고리 위치 등을 수집할 수 있습니다. __NEXT_DATA__ 방식을 쓰면 이런 데이터가 모두 구조화된 JSON으로 제공됩니다. 내부 API 가로채기를 쓰면 옵션별 가격, 실시간 재고 수량, 페이지네이션된 리뷰 데이터까지 추가로 얻을 수 있습니다.

Walmart를 크롤링하려면 프록시가 꼭 필요한가요?

네, 실무용이나 반복 수집에서는 사실상 필수입니다. 합니다. 헤더를 완벽하게 맞춰도 비주거용 IP는 Akamai의 IP 평판 시스템에 걸립니다. 주거용 또는 모바일 프록시가 필요합니다. 데이터센터 IP는 거의 즉시 소진됩니다. 프록시 제공업체와 요금제에 따라 1,000페이지당 대략 $3~$17 정도를 예상하세요.

코딩 없이 Walmart를 크롤링할 수 있나요?

네. 은 AI 기반 Chrome 확장 프로그램으로, 두 번의 클릭만으로 Walmart를 수집합니다. "AI Suggest Fields"로 상품 데이터 열을 자동 감지한 뒤 "Scrape"를 누르면 데이터를 추출합니다. 클라우드에서 안티봇을 처리하고 Excel, Google Sheets, Airtable, Notion으로 바로 내보낼 수 있으며, 모두 무료입니다. 빠르게 데이터를 얻고 싶지만 커스텀 파이프라인을 만들고 싶지 않은 분석가, PM, 비즈니스 사용자에게 적합합니다. 고볼륨이나 고도 커스터마이징이 필요하다면 Python이나 스크래핑 API가 더 맞습니다.

AI로 Walmart 스크래핑을 시작해 보세요

더 알아보기

Shuai Guan
Shuai Guan
Co-founder/CEO @ Thunderbit. Passionate about cross section of AI and Automation. He's a big advocate of automation and loves making it more accessible to everyone. Beyond tech, he channels his creativity through a passion for photography, capturing stories one picture at a time.
목차

Thunderbit 사용해보기

리드와 기타 데이터를 단 2번 클릭으로 수집하세요. AI로 구동됩니다.

Thunderbit 받기 무료예요
AI로 데이터 추출하기
Google Sheets, Airtable, Notion으로 데이터를 쉽게 옮기세요
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week