2026년 Python으로 Walmart 스크래핑하는 방법(차단당하지 않고)

최종 업데이트: June 29, 2026
2026년 Python으로 Walmart 스크래핑하는 방법(차단당하지 않고)

Walmart는 같은 상품 가격을 하루에도 몇 번씩 바꿔요. 코드로 추적해 본 분이라면 어떤 고생인지 아실 거예요. 스크립트가 20분쯤 잘 돌다가, 어느 순간 멀쩡한 200 OK처럼 위장한 CAPTCHA 페이지를 슬그머니 뱉거든요.

저는 Thunderbit에서 데이터 추출을 맡으며 Walmart 안티봇과 오래 씨름했어요. 그 과정에서 알게 된 걸 정리해 드릴게요. 2025년에 통하는 방법, 데이터를 조용히 망치는 함정, 직접 짤지 스크래핑 API를 쓸지 노코드를 쓸지의 솔직한 득실까지요. 다룰 건 추출 방식 세 가지(HTML 파싱, __NEXT_DATA__ JSON, 내부 API 가로채기), 거의 모든 튜토리얼이 빼먹는 운영용 오류 처리, 상황별 판단 기준이에요. Python으로 짜든 점심 전에 가격 스프레드시트만 뽑든 쓸모가 있을 거예요.

왜 Python으로 Walmart를 스크래핑해야 할까요?

Walmart는 매출 기준 세계 1위 소매업체예요. FY2025 매출 6,809억 8,500만 달러Fortune Global 500 12년 연속 1위죠. 활성 상품 목록은 약 4억 2천만 개이고, CFO는 마켓플레이스에 “5억 개의 SKU”가 있다고 밝혔어요. 이 중 약 95%가 제3자 판매자 물량이라 카탈로그가 굉장히 유동적이에요. 판매자도, 옵션도, 재고도 매일 출렁이거든요.

walmart_stats_670d06c6bd.png

스크래핑이 의미 있는 이유가 바로 이 변동성이에요. 밤새 돌린 스크래퍼가 잡는 변화를 분기 보고서로는 담을 수 없으니까요. 제가 현장에서 자주 보는 활용 사례는 이래요.

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

경쟁사 가격 모니터링 소프트웨어 시장만 봐도 2025년 19억 2천만 달러 규모이고, 2033년이면 50억 9천만 달러까지 큰다는 전망이에요. 소비자 행동이 이 지출을 떠받쳐요. 온라인 쇼핑객의 94%가 가격을 비교하고, 83%는 여러 사이트를 넘나들며 비교해요.

이 분야의 기본 언어는 Python이에요. Apify의 2026 인프라 보고서를 보면 전체 웹 스크래핑의 71.7%가 Python이고, 핵심 라이브러리 requests는 주당 약 3천만 회 다운로드돼요. 어느 정도 규모로 긁는다면 거의 Python을 쓴다고 봐도 돼요.

Walmart가 가장 스크래핑하기 어려운 사이트 중 하나인 이유

Walmart가 특히 까다로운 건 상용 안티봇 제품 두 개를 연달아 쓰기 때문이에요. 엣지 WAF와 TLS 핑거프린팅을 맡는 Akamai Bot Manager가 1차, 행동 기반 JavaScript 챌린지를 거는 PerimeterX(현 HUMAN Security)가 2차예요. Scrape.do는 이 조합을 두고 “우회 사례가 매우 드물고 극도로 어렵다”고 했어요.

walmart_antibot_3d67d0119c.png

ScrapeOps는 Walmart의 전체 난이도를 9/10으로 봤고, Akamai 단독으로도 9/10이에요. 제 경험상 이 평가는 꽤 정확해요.

실전에서 마주치는 방어는 이래요.

Akamai Bot Manager는 TLS 핑거프린트(JA3/JA4 해시), HTTP/2 프레임 순서, 헤더 순서와 대소문자, 세션 쿠키(_abck, ak_bmsc)를 봐요. 기본 Python requests는 실제 브라우저라면 안 나올 TLS 핑거프린트를 흘리니, 요청이 Walmart 서버에 닿기도 전에 끊겨요.

PerimeterX/HUMAN은 Akamai 다음 차례예요. navigator 속성, canvas, WebGL, 오디오 컨텍스트, 마우스·스크롤·키 입력 같은 행동 생체 신호를 살피는 JavaScript 핑거프린팅(px.js)을 돌려요. 눈에 보이는 형태가 악명 높은 “Press & Hold” 챌린지예요. 버튼을 약 10초 누르는 동안 행동 신호를 표집하죠. Oxylabs는 “Walmart의 PerimeterX ‘Press & Hold’ CAPTCHA는 코드로 풀기 거의 불가능한 것으로 알려져 있다”고 못 박았어요.

진짜 무서운 건 조용한 차단이에요. Walmart는 403 대신 CAPTCHA 본문을 담은 HTTP 200을 줘요. ScrapingBee도 같은 얘기예요. “Walmart는 CAPTCHA 페이지에도 200 OK를 반환한다. 성공 여부를 상태 코드만으로 판단할 수 없다.” 스크립트는 CAPTCHA HTML을 “상품 없음”으로 착각하고 넘어가요. 데이터 절반이 쓰레기가 돼도 본인은 몰라요.

여기에 매장 범위 데이터 문제도 있어요. Walmart 가격과 재고는 위치별로 다르고, locDataV3assortmentStoreId 같은 쿠키가 좌우해요. 올바른 쿠키가 없으면 겉보기엔 멀쩡하지만 실제 쇼핑객 화면과 어긋나는 “전국 기본 데이터”를 받아요. 쿠키 누락은 차단 페이지를 안 띄워요. 대신 티 안 나게 잘못된 데이터를 만들죠. 이게 더 고약해요.

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

단계별로 들어가기 전에 주요 추출 방식 세 가지를 먼저 볼게요. 경쟁 튜토리얼은 보통 한두 개만 다루지만, 여기서는 셋 다 펼쳐 놓고 상황에 맞게 고르게 해드릴게요.

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

Walmart에서 HTML 파싱은 최악의 선택이에요. 사이트가 Next.js 번들을 배포하는데, CSS 클래스명이 해시 처리돼 있어 배포할 때마다 바뀌거든요. __NEXT_DATA__ JSON 방식이 2024~2026년 진지한 오픈소스 Walmart 스크래퍼들이 공통으로 쓰는 현실적 선택이에요. 내부 API 가로채기는 가장 강력하지만, 대부분 튜토리얼이 슬쩍 넘기는 주의점이 있어요. 그리고 맞춤 파이프라인이 아예 필요 없다면 Thunderbit가 답이에요.

Walmart 스크래핑용 Thunderbit 사용해 보기

Walmart 스크래핑을 위한 Python 환경 설정

준비물은 이래요.

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

프로젝트 폴더와 가상 환경부터 만드세요.

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

필요한 라이브러리를 설치하세요.

pip install curl_cffi parsel beautifulsoup4 lxml

curl_cffi는 2025년 까다로운 사이트 스크래핑의 표준이에요. 정확한 브라우저 TLS 핑거프린트를 흉내 내는 libcurl 바인딩이거든요. Bright Data도 “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가 공통으로 쓰는 표준 헤더 세트는 이래요.

from curl_cffi import requests

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/124.0.0.0 Safari/537.36"
    ),
    "Accept": (
        "text/html,application/xhtml+xml,application/xml;q=0.9,"
        "image/avif,image/webp,*/*;q=0.8"
    ),
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
    "Referer": "https://www.google.com/",
}

session = requests.Session(impersonate="chrome124")
url = "https://www.walmart.com/ip/Apple-AirPods-Pro-2nd-Generation/1752657021"
response = 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만 보면 안 돼요.

운영 환경이거나 반복 사용이라면 주거용 프록시가 필요해요. 데이터센터 IP는 Akamai의 IP 평판 시스템에 곧장 걸려 소진돼요. 자세한 오류 처리와 프록시 전략은 아래 운영 섹션에서 짚을게요.

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

Walmart.com은 Next.js 앱이라, 서버 렌더링 HTML이 전체 하이드레이션 페이로드를 단일 script 태그에 담아요. <script id="__NEXT_DATA__" type="application/json">이 바로 그 핵심이에요.

Scrapfly의 2026 가이드도 같은 얘기를 해요. “2026년의 Walmart는 __NEXT_DATA__ script 태그 안에 구조화된 JSON을 담은 Next.js를 쓰므로, 숨겨진 데이터 추출이 CSS 셀렉터 파싱보다 안정적이다.” 이름난 오픈소스 스크래퍼 — Scrapfly, Oxylabs, python-scrapy-playbook — 가 다 이 방식이에요.

추출은 이렇게 해요.

import json
from parsel import Selector

sel = Selector(text=response.text)
raw = sel.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
data = json.loads(raw)
product = data["props"]["pageProps"]["initialData"]["data"]["product"]
idml = data["props"]["pageProps"]["initialData"]["data"].get("idml", {})

대부분 튜토리얼은 여기서 끝이에요. 하지만 실무에서 진짜 필요한 필드를 위한 완전한 JSON 경로 맵이 따로 있어요. 2024~2026년 실제 Walmart 페이지로 검증했어요.

데이터 필드JSON 경로(initialData 아래)유형비고
상품명data > product > name문자열
브랜드data > product > brand문자열
현재 가격(숫자)data > product > priceInfo > currentPrice > price실수매장 쿠키에 따라 다를 수 있음
현재 가격(문자열)data > product > priceInfo > currentPrice > priceString문자열예: "$9.99" 형태
짧은 설명data > product > shortDescriptionHTML 문자열텍스트로 쓰려면 BeautifulSoup으로 파싱
긴 설명data > idml > longDescriptionHTML 문자열product 안이 아니라 idml에 있음 — 예전 튜토리얼이 자주 틀리는 함정
모든 이미지data > product > imageInfo > allImages배열{id, url} 객체 목록
평균 평점data > product > averageRating실수키는 rating이 아니라 averageRating
리뷰 수data > product > numberOfReviews정수
옵션data > product > variantCriteria배열크기, 색상 같은 옵션 그룹
재고 상태data > product > availabilityStatus문자열IN_STOCK, OUT_OF_STOCK, LIMITED_STOCK
판매자data > product > sellerDisplayName문자열
제조사data > product > manufacturerName문자열

longDescription 경로가 사람들이 자주 헛짚는 함정이에요. 2023년 ScrapeHero 글은 product.longDescription에 있다고 했지만, 2024년 이후 자료들은 일관되게 형제 키인 idml에 둬요. 항상 idml.longDescription을 먼저 읽고, 옛 페이지에만 product.longDescription으로 대체하세요.

.get() 체인을 쓴 안전한 추출 패턴은 이래요.

def extract_product(data):
    product = data["props"]["pageProps"]["initialData"]["data"]["product"]
    idml = data["props"]["pageProps"]["initialData"]["data"].get("idml", {})

    price_info = product.get("priceInfo", {})
    current_price = price_info.get("currentPrice", {})
    image_info = product.get("imageInfo", {})

    return {
        "name": product.get("name"),
        "brand": product.get("brand"),
        "price": current_price.get("price"),
        "price_string": current_price.get("priceString"),
        "short_desc": product.get("shortDescription"),
        "long_desc": idml.get("longDescription", product.get("longDescription")),
        "images": [img.get("url") for img in image_info.get("allImages", [])],
        "rating": product.get("averageRating"),
        "review_count": product.get("numberOfReviews"),
        "variants": product.get("variantCriteria"),
        "availability": product.get("availabilityStatus"),
        "seller": product.get("sellerDisplayName"),
        "manufacturer": product.get("manufacturerName"),
    }

JSON 경로를 직접 다루기 싫다면, Thunderbit의 AI가 이 필드들을 알아서 식별하고 구조화해 줘요. 수동 경로 매핑이 필요 없어요. “AI 필드 추천”을 누르면 페이지를 읽고 표를 만들어 주죠. 다만 맞춤 파이프라인을 짠다면 위 맵이 기준이 돼요.

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

이 방법을 제대로 다룬 경쟁 글은 거의 없어요. 가장 강력한 경로이지만 가장 복잡하기도 하니까요.

Walmart 프런트엔드는 Apollo Gateway 기반의 연합 GraphQL 백엔드를 호출해요. 엔드포인트는 www.walmart.com/orchestra/* 아래에 있어요.

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

이들은 __NEXT_DATA__가 가끔 잘라내는 데이터까지 담은 깔끔한 구조화 JSON을 돌려줘요. 옵션별 가격, 실시간 재고 수량, 전체 리뷰 페이지네이션 같은 것들이죠.

블로그 글들이 빙빙 돌며 피하는 핵심 문제가 있어요. Walmart는 Apollo persisted query를 써요. 요청 본문에 쿼리 텍스트가 아니라 SHA-256 해시(persistedQuery.sha256Hash)만 들어가요. 서버가 그 해시를 모르면 PersistedQueryNotFound가 뜨고요. Walmart는 배포 때마다 이 해시를 바꿔요. 그래서 이름난 오픈소스 스크래퍼들이 /orchestra/ 코드를 복붙용으로 공개하지 않는 거예요.

이 방법을 솔직하고 실용적으로 쓰는 길은 DevTools 작업이에요.

  1. Chrome에서 Walmart 상품 페이지를 열어요
  2. DevTools → Network 탭을 열고 “Fetch/XHR”로 필터링해요
  3. 페이지를 정상적으로 탐색해요 — 옵션을 누르고, 리뷰까지 스크롤하고, 매장 위치를 바꿔 보세요
  4. 상품 데이터를 반환하는 /orchestra/* 엔드포인트 요청을 찾아요
  5. 요청을 오른쪽 클릭 → “Copy as cURL”을 선택해요
  6. curl_cffi를 사용해 cURL 명령을 Python으로 변환해요

재생한 API 호출은 이렇게 생겼어요.

import json
from curl_cffi import requests

session = requests.Session(impersonate="chrome124")

# 먼저 상품 페이지를 방문해 세션을 워밍업합니다
session.get("https://www.walmart.com/ip/some-product/1234567", headers=HEADERS)

# 그런 다음 내부 API 호출을 재생합니다(DevTools에서 복사)
api_url = "https://www.walmart.com/orchestra/pdp/graphql"
api_headers = {
    **HEADERS,
    "accept": "application/json",
    "content-type": "application/json",
    "referer": "https://www.walmart.com/ip/some-product/1234567",
    "wm_qos.correlation_id": "복사해 둔 correlation-id",
}
payload = {
    # DevTools에서 정확한 요청 본문을 붙여 넣으세요
    "variables": {"productId": "1234567"},
    "extensions": {
        "persistedQuery": {
            "version": 1,
            "sha256Hash": "복사한 해시값"
        }
    }
}

api_response = session.post(api_url, headers=api_headers, json=payload)
api_data = api_response.json()

세션 워밍업 단계가 정말 중요해요. Walmart의 PerimeterX 쿠키(_px3, _pxhd, ACID)는 API 호출이 성공하기 전에 초기 HTML 가져오기로 설정돼야 하거든요. 없으면 412나 403이 떨어져요.

언제 이 방법을 쓸까요? __NEXT_DATA__에 없는 데이터가 필요할 때예요. 상세 옵션 가격, 첫 배치를 넘는 리뷰, 실시간 재고 수량 같은 거죠. 대부분 활용 사례는 __NEXT_DATA__만으로 충분하고 훨씬 간단해요.

Walmart 검색 결과와 여러 페이지 스크래핑하기

검색 결과도 비슷한 __NEXT_DATA__ 패턴을 따르지만, JSON 경로는 달라요.

search_url = "https://www.walmart.com/search?q=laptops&page=1"
response = session.get(search_url, headers=HEADERS)

sel = Selector(text=response.text)
raw = sel.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
data = json.loads(raw)

search_result = data["props"]["pageProps"]["initialData"]["searchResult"]
items = search_result["itemStacks"][0]["items"]

# 광고 상품 제외
organic_items = [i for i in items if i.get("__typename") == "Product"]

for item in organic_items:
    print(item.get("name"), item.get("priceInfo", {}).get("currentPrice", {}).get("price"))

페이지네이션은 page 매개변수를 키우면 돼요. &page=1, &page=2 식이죠. 다만 문서에 안 적힌 제한이 하나 있어요. Walmart는 실제 총 페이지 수와 상관없이 검색 결과를 25페이지로 제한해요. Scrapfly도 확인했어요. “Walmart는 총 페이지 수와 관계없이 접근 가능한 결과 페이지 수의 최대치를 25로 설정한다.”

더 깊게 긁으려면 이런 우회법을 써요.

  • 정렬 순서 바꾸기: 같은 검색어를 &sort=price_low&sort=price_high로 따로 돌려 약 50페이지 분량 확보
  • 가격 범위 분할: &min_price=X&max_price=Y로 카탈로그를 작은 구간으로 쪼개기
  • 카테고리 분할: 사이트 전체가 아니라 특정 카테고리 안에서 검색하기

itemStacks가 배열이라는 점도 기억하세요. Scrapfly는 레포에서 [0]을 하드코딩하지만, 카테고리나 탐색 페이지에는 종종 여러 스택(“Top picks”, “More results”)이 있어요. 더 견고하게 가려면 모든 스택을 순회하세요.

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

또 하나, Walmart의 robots.txt/search를 금지해요. 반면 상품 상세(/ip/...)와 대부분의 카테고리(/cp/...)는 금지 대상이 아니에요. 준수가 걱정된다면 검색 페이지보다 상품 페이지와 카테고리 트리부터 시작하세요.

조용한 차단이 데이터를 망치지 않게 하세요: 운영용 오류 처리

대부분 튜토리얼이 여기서 무너져요. 한 페이지 가져오고 한 상품 파싱하고 끝내거든요. 하지만 운영에선 수천 페이지를 가져와야 하고, Walmart는 적극적으로 막아요. 데모용과 실전 스크래퍼를 가르는 건 결국 실패를 다루는 방식이에요.

데이터가 오염되기 전에 조용한 차단 감지하기

Walmart 스크래퍼에서 가장 중요한 함수는 차단 감지기예요. ScrapingBee, Scrapingdog, Oxylabs, Decodo의 공통 견해를 모으면, 독립적인 검사 4가지를 해야 해요.

BLOCK_MARKERS = (
    "Robot or human",
    "Press &amp; Hold",
    "Press & Hold",
    "px-captcha",
    "perimeterx",
)

def is_walmart_blocked(response) -> bool:
    # 1. 전용 차단 엔드포인트로 리디렉션됨
    if "/blocked" in str(response.url):
        return True
    # 2. 명시적 상태 코드
    if response.status_code in (403, 412, 428, 429, 503):
        return True
    # 3. CAPTCHA 본문이 포함된 200 OK(조용한 차단 사례)
    body = response.text or ""
    if any(m.lower() in body.lower() for m in BLOCK_MARKERS):
        return True
    # 4. 응답 길이 점검 — 실제 PDP는 보통 300~900KB
    if len(response.content) < 50_000 and "/ip/" in str(response.url):
        return True
    return False

네 번째 검사인 응답 길이 확인은, 눈에 띄는 CAPTCHA 표시는 없는데 정작 상품 데이터도 빠진 축약 페이지를 Walmart가 줄 때 잡아내요.

지수적 백오프와 지터를 포함한 재시도 로직

요청이 실패하자마자 곧바로 Walmart를 두드리면 안 돼요. 표준 패턴은 지수적 백오프와 지터로 재시도 타이밍을 흩어 놓는 거예요.

import time
import random
import logging
from curl_cffi import requests as cffi_requests

log = logging.getLogger("walmart")

def fetch_with_retry(session, url, max_retries=5, base_delay=2, max_delay=60):
    for attempt in range(max_retries):
        try:
            response = session.get(url, headers=HEADERS, timeout=15)

            if response.status_code in (429, 503):
                raise Exception(f"Throttled: {response.status_code}")

            if is_walmart_blocked(response):
                raise Exception("조용한 차단 감지")

            return response

        except Exception as e:
            if attempt == max_retries - 1:
                raise

            wait = min(max_delay, base_delay * (2 ** attempt)) + random.uniform(0, 3)
            log.warning(f"시도 {attempt + 1} 실패: {e}. {wait:.1f}초 후 재시도합니다")
            time.sleep(wait)

    return None

지터(random.uniform(0, 3))는 장식이 아니에요. 여러 작업자가 같은 초에 동시에 재시도해서 Akamai의 속도 감지기에 걸리는 걸 막아 주거든요.

속도 제한

ThunderbitScrape.do 둘 다 Walmart에는 요청당 3~6초의 무작위 지연으로 의견이 모여요. “페이지를 부를 때마다 3~6초 기다리고 지연을 랜덤화하라”는 거죠.

import time
import random

def rate_limited_fetch(session, url):
    response = fetch_with_retry(session, url)
    time.sleep(random.uniform(3.0, 6.0))
    return response

규모가 커지면 비동기 속도 제한용으로 aiolimiter를 검토하세요.

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

데이터 검증

차단을 안 당했더라도 파싱된 데이터가 틀릴 수 있어요(잘못된 매장, 저하된 페이로드). 출력에 쓰기 전에 검증하세요.

def validate_product(product):
    """상품 데이터가 그럴듯하면 True를 반환합니다."""
    if not product.get("name"):
        return False

    price = (product.get("priceInfo") or {}).get("currentPrice", {}).get("price")
    if not isinstance(price, (int, float)) or price <= 0:
        return False

    if product.get("availabilityStatus") not in ("IN_STOCK", "OUT_OF_STOCK", "LIMITED_STOCK"):
        return False

    return True

세션 로깅

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

class ScrapeMetrics:
    def __init__(self):
        self.total = 0
        self.success = 0
        self.blocks = 0
        self.errors = 0

    def record(self, result):
        self.total += 1
        if result == "success":
            self.success += 1
        elif result == "blocked":
            self.blocks += 1
        else:
            self.errors += 1

    @property
    def success_rate(self):
        return (self.success / self.total * 100) if self.total > 0 else 0

    def check_health(self):
        if self.total > 20 and self.success_rate < 80:
            log.critical(f"성공률이 {self.success_rate:.1f}%로 떨어졌어요 — 프록시를 교체하거나 잠시 멈추는 것을 고려하세요")

화려하진 않아요. 그래도 데이터를 깨끗하게 지켜 줘요.

직접 Python으로 할까, 스크래핑 API를 쓸까, 노코드를 쓸까: Walmart 스크래핑 방식 고르기

많은 개발자가 이게 맞는 선택인지 따져보지도 않고 곧장 맞춤 스크래퍼부터 짜기 시작해요. ScrapeOps는 Walmart 난이도를 9/10으로 봤고, 포럼에서도 “기본이 9/10”이라며 “전용 스크래핑 API가 과한 거 아니냐”를 고민하죠. 답은 물량, 예산, 엔지니어링 역량에 달려 있어요.

요소DIY Python(requests + 프록시)스크래핑 API(Oxylabs, Bright Data 등)노코드 도구(Thunderbit)
첫 행까지 걸리는 설정 시간몇 시간15~60분약 2분
운영 가능 상태까지 걸리는 시간40~80시간4~16시간약 30분
안티봇 처리직접 관리(어려움)제공업체가 처리자동 처리
소규모 비용(<월 1K 페이지)낮음(프록시 비용 약 $4~8/GB)$40~$49/월 시작 요금무료~$15/월
대규모 비용(월 10만+ 페이지)요청당 더 저렴요청당 더 비쌈달라짐
커스터마이징완전 제어API 매개변수UI/필드 범위 내 제한
지속 유지보수월 4~8시간거의 없음없음(AI가 적응)
가장 적합한 대상맞춤형 파이프라인을 만드는 개발자중간 규모 운영 스크래핑비즈니스 사용자, 빠른 단발성 추출

DIY Python이 맞는 경우

이미 프록시 계약이 있거나, 헤더·우편번호 타겟팅·판매자 집단을 빡빡하게 제어해야 하거나, 월 수백만 페이지를 인덱싱해 레코드당 API 요금이 부담되거나, 온프레미스나 컴플라이언스 보장이 필요할 때 DIY가 유리해요. 대신 실제 엔지니어링 시간이 들어가요. 페이지네이션, 재시도, 프록시 로테이션, TLS 위장, 여러 페이지 스키마를 갖춘 운영용 Scrapy 스파이더는 숙련 Python 작업 40~80시간이 들고, Walmart가 핑거프린트를 바꿀 때마다 월 4~8시간 유지보수도 따라와요.

스크래핑 API가 시간을 절약해 주는 경우

스크래핑 API는 안티봇 계층을 대신 처리해 줘요. ScrapeOps 벤치마크를 보면 Walmart에서 Zyte API는 99%, Scrape.do는 98% 성공률이에요. ScraperAPI, Oxylabs, Scrapingdog 같은 도구의 입문 요금은 월 $40~$49고요. 엔지니어 25명 팀이 월 1만100만 페이지를 긁는다면 API가 거의 항상 정답이에요. 요청당 비용을 내는 대신 유지보수를 거의 없애는 거죠.

노코드가 맞는 경우

Thunderbit는 완전히 다른 부류에 맞아요. 다음 스프린트가 아니라 오늘 오후 안에 Walmart 상품 데이터를 스프레드시트로 받아야 하는 PM, 애널리스트, 이커머스 운영자라면 노코드 도구가 솔직한 답이에요.

흐름은 이래요. Thunderbit Chrome 확장 프로그램을 설치하고, Walmart 상품 또는 검색 페이지로 가서 “AI 필드 추천”을 누르세요. 그러면 Thunderbit의 AI가 페이지를 읽고 상품명, 가격, 평점 같은 열을 제안해 줘요. “스크래핑”을 누르면 데이터가 표로 채워지고요. Excel, Google Sheets, Airtable, Notion으로 모두 무료 내보내기가 돼요. 결제벽도 없어요.

Thunderbit는 클라우드에서 안티봇을 처리하니까 CAPTCHA, 프록시, TLS 핑거프린팅을 직접 만질 필요가 없어요. AI가 레이아웃 변경에 알아서 적응하니 유지보수도 없죠. JSON 경로 자체를 손대고 싶지 않은 분에게 가장 부담 적은 길이에요.

솔직한 한계도 있어요. Thunderbit는 하루 10만+ 페이지용으로 설계된 도구가 아니에요. 크레딧 예산과 클라우드 상한 탓에 대량 수집은 원시 API만큼 경제적이지 않을 수 있어요. 또 도구가 지원하지 않는 한 특정 우편번호나 ASN을 고정할 수도 없고요. 지속적이고 대규모인 파이프라인이라면 DIY나 스크래핑 API가 여전히 나아요.

대략적인 비용 계산도 해볼게요. Thunderbit로 Walmart 상품 1,000행을 긁으면 대략 2,000 크레딧이 들고, Starter/Pro 요금제 기준 약 $0.60~$1.10 정도예요. 낮은 물량에서는 Oxylabs의 Walmart API와 비슷하고, 대부분의 저가 스크래핑 API보다 저렴해요. 최신 내용은 Thunderbit 요금제에서 확인하세요.

AI로 Walmart 상품 데이터 스크래핑하기 Get Started Free

스크래핑한 Walmart 데이터 내보내기

데이터를 얻었으면 이제 쓸모 있는 곳에 저장할 차례예요. 보통 형식 세 가지면 충분해요.

CSV — 분석가들이 실제로 여는 가장 기본 형식이에요.

import csv

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

Excel 호환을 위해 utf-8-sig 인코딩을 쓰세요. BOM 마커가 Excel의 문자 깨짐을 막아 줘요.

JSONL — 스크래핑 파이프라인의 운영 표준 형식이에요.

import json
import gzip

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

JSONL은 충돌에 강하고(쓰기 도중 끊겨도 마지막 줄만 잃음), 일정한 메모리로 스트리밍할 수 있고, 옵션이나 리뷰 같은 중첩 데이터도 그대로 보존해 줘요.

Excel — 한 번 넘기는 분석용이에요.

from openpyxl import Workbook

def export_excel(products, filename="walmart_products.xlsx"):
    wb = Workbook(write_only=True)
    ws = wb.create_sheet("Products")
    ws.append(["이름", "가격", "재고 상태", "평점", "리뷰 수", "판매자"])
    for p in products:
        ws.append([p.get("name"), p.get("price"), p.get("availability"),
                    p.get("rating"), p.get("review_count"), p.get("seller")])
    wb.save(filename)

Thunderbit는 Python을 안 쓰는 분에게도 내보내기 흐름을 줘요. Google Sheets, Airtable, Notion, Excel, CSV, JSON으로 한 번에 내보낼 수 있고, 기본 요금제에서는 다 무료예요. 지속 모니터링이 필요하면 Thunderbit의 예약 스크래퍼로 반복 추출도 자동화할 수 있고요.

스케줄링에서 하나 주의할 게 있어요. Walmart 스크래핑에 GitHub Actions를 쓰지 마세요. GitHub Actions 러너는 Walmart 안티봇이 즉시 차단하는 Azure IP 대역에 있거든요. VPS에서 APScheduler를 쓰거나, 모든 트래픽을 주거용 프록시로 보내세요.

Walmart 스크래핑의 법적·윤리적 가이드라인

포럼 사용자들도 이 점을 분명히 걱정해요. “개발자랑 숨바꼭질하는 건 괜찮은데, 법무팀 상대는 좀 조심스럽다”는 식이죠.

Walmart 이용약관명시적으로 금지해요. “사전 서면 동의 없이” 로봇, 스파이더, 기타 수동/자동 장치로 자료를 가져오고, 인덱싱하고, ‘scrape’·‘data mine’하거나 그 밖의 방식으로 수집하는 행위를 막죠.

Walmart robots.txt/search, /account, /api/, 그리고 수십 개 내부 엔드포인트를 금지해요. 상품 상세(/ip/...)와 리뷰(/reviews/product/)는 금지 대상이 아니고요.

hiQ 대 LinkedIn 판례(제9연방순회항소법원, 2022)는 공개 접근 데이터 스크래핑이 연방 CFAA를 위반하지 않을 가능성이 높다고 봤어요. 다만 같은 법원은 뒤에 hiQ가 LinkedIn의 스크래핑 금지 계약을 위반했다고 보고 50만 달러 동의 판결을 내렸죠. 더 최근인 2024년 판결들(Meta 대 Bright Data, X Corp. 대 Bright Data)은 CFAA 범위를 더 좁히고 저작권 선점 방어를 인정했지만, Walmart에 딱 들어맞지 않는 특정 약관 문구에 좌우됐어요.

실무 지침은 이래요. 서버를 과하게 두드리지 말고, 속도 제한을 지키고, 개인 정보나 사용자 데이터는 긁지 말고, 데이터는 책임 있게 쓰세요. 개인 연구로 공개 페이지를 적당히 긁는 것과 약관을 거스르며 상업 규모로 추출하는 건 위험 프로필이 완전히 달라요. Walmart 데이터로 제품을 만들 계획이라면 변호사와 상의하고 공식 제휴 및 판매자 API도 살펴보세요.

면책 고지: 이 내용은 교육용 정보이며 법률 자문이 아니에요.

결론과 핵심 요약

Python으로 Walmart를 긁는 일은 이중 방어 체계인 Akamai + PerimeterX 때문에 난이도 9/10 수준이에요. 불가능하진 않지만, 맞는 도구와 패턴이 있어야 해요.

핵심만 추리면 이래요.

  • 대부분의 경우 __NEXT_DATA__ JSON 추출이 가장 현실적이에요. 2024~2026년 진지한 오픈소스 스크래퍼들이 다 쓰는 방식이죠. PDP 기본 경로는 props.pageProps.initialData.data.product, 검색/탐색은 searchResult.itemStacks예요.
  • impersonate="chrome124"curl_cffi는 필수예요. 일반 requestshttpx는 헤더와 무관하게 Akamai의 TLS 핑거프린팅을 못 뚫어요.
  • 진짜 위험은 조용한 차단이에요. Walmart는 CAPTCHA 본문에 200 OK를 붙여요. 상태 코드만 보지 말고 응답 내용을 확인하세요.
  • 운영용 스크래퍼는 성공 경로 코드만으론 부족해요. 지터 있는 지수적 백오프, 신호 4가지를 쓴 차단 감지, 요청당 3~6초 속도 제한, 데이터 검증, 세션 상태 모니터링이 다 필요해요.
  • /orchestra/* 내부 API 가로채기는 강력하지만 취약해요. 주력 방식이 아니라, 특정 데이터가 필요할 때 DevTools 연습용으로 쓰세요.
  • Walmart는 검색 결과를 25페이지로 제한해요. 정렬 변경과 가격 범위 분할로 더 넓게 커버하세요.
  • 방식은 솔직하게 고르세요. 맞춤 요구와 대량 처리에는 DIY Python, 스크래핑 엔지니어가 없는 중간 규모 팀에는 스크래핑 API, 오늘 오후에 Google Sheets로 데이터를 받고 싶은 비즈니스 사용자에게는 Thunderbit가 적합해요.

노코드를 써보고 싶다면 Thunderbit Chrome 확장 프로그램에 무료 플랜이 있어요. 페이지 몇 개를 긁어 결과를 직접 보세요. Python을 택한다면 이 글의 코드 패턴은 운영에서 검증된 것들이고요. 어느 쪽이든 이제 Walmart의 방어 체계와 그 사이를 통과하는 세 갈래 길을 알게 됐어요.

웹 스크래핑 기법을 더 배우고 싶다면 Python으로 웹 스크래핑하는 방법, 최고의 자동 웹 스크래핑 도구, 차단 없이 웹 스크래핑하는 방법 가이드를 확인해 보세요. Thunderbit YouTube 채널에서 튜토리얼도 볼 수 있어요.

자주 묻는 질문

Walmart 상품 데이터를 스크래핑하는 것은 합법인가요?

Walmart 이용약관은 서면 동의 없는 자동 스크래핑을 금지해요. 제9연방순회항소법원의 hiQ 대 LinkedIn 판결(2022)은 연방 CFAA가 공개 페이지 스크래핑에 적용될 가능성이 낮다고 정리했지만, 같은 사건은 스크래퍼에 대한 50만 달러 계약 위반 판결로 끝났어요. 개인 연구로 공개 상품 페이지를 적당한 속도로 긁는 것과 상업 규모로 추출하는 건 위험도가 완전히 달라요. Walmart 데이터로 사업을 만든다면 변호사와 상의하세요.

왜 제 Walmart 스크래퍼는 계속 차단되나요?

가장 흔한 원인은 일반 requestshttpx 사용(Python 고유의 TLS 핑거프린트가 Akamai에 바로 걸림), 잘못됐거나 빠진 헤더, 프록시 로테이션 없음, 페이지당 3~6초보다 빠른 요청 속도, 세션 쿠키(_px3, _abck, locDataV3) 누락이에요. 이 글에서 설명한 impersonate="chrome124"curl_cffi로 바꾸고, 주거용 프록시를 쓰며, 차단 감지와 재시도 패턴을 구현하세요.

Python으로 Walmart에서 어떤 데이터를 스크래핑할 수 있나요?

상품명, 가격(현재가 및 할인 전 가격), 이미지, 짧은 설명과 긴 설명, 평점, 리뷰 수, 재고 상태, 판매자 이름, 제조사 정보, 옵션(사이즈, 색상), 카테고리 위치를 가져올 수 있어요. __NEXT_DATA__ 방식이면 이 정보가 모두 구조화된 JSON으로 나와요. 내부 API 가로채기를 쓰면 옵션별 가격, 실시간 재고 수량, 페이지가 나뉜 리뷰 데이터까지 더 얻을 수 있고요.

Walmart를 스크래핑하려면 프록시가 필요한가요?

네, 운영 환경이나 반복 사용에서는 필요해요. Walmart의 안티봇 시스템은 일반 요청을 꾸준히 차단해요. 헤더를 완벽히 맞춰도 비주거용 IP는 Akamai의 IP 평판 시스템에 걸려요. 주거용이나 모바일 프록시가 필요하고, 데이터센터 IP는 거의 즉시 소진돼요. 프록시 제공업체와 요금제에 따라 1,000페이지당 대략 $3~$17를 잡으면 돼요.

코드 없이 Walmart를 스크래핑할 수 있나요?

네. Thunderbit는 AI 기반 Chrome 확장 프로그램으로 Walmart를 두 번 클릭으로 긁어요. “AI 필드 추천”으로 상품 데이터 열을 자동 감지하고, 이어 “스크래핑”으로 데이터를 추출하죠. 안티봇 챌린지는 클라우드에서 처리하고, Excel, Google Sheets, Airtable, Notion으로 바로 내보낼 수 있어요 — 모두 무료예요. 빠르게 데이터를 받아야 하는 애널리스트, PM, 비즈니스 사용자에게 가장 잘 맞고, 맞춤 파이프라인이나 고도로 커스터마이즈된 스크래핑에는 Python이나 스크래핑 API가 더 나아요.

AI Walmart 스크래핑에 Thunderbit 사용해 보기 Get Started Free

더 알아보기

Shuai Guan
Shuai Guan
Thunderbit CEO | AI 데이터 자동화 전문가 Shuai Guan은 Thunderbit의 CEO이자 University of Michigan 공학과 출신입니다. 10년 가까운 기술 및 SaaS 아키텍처 경험을 바탕으로, 복잡한 AI 모델을 누구나 바로 활용할 수 있는 노코드 데이터 추출 도구로 바꾸는 데 전문성을 갖고 있습니다. 이 블로그에서는 웹 스크래핑과 자동화 전략에 대한 필터링 없는 실전 경험과 검증된 인사이트를 공유하며, 더 똑똑하고 데이터 중심적인 워크플로를 만드는 데 도움을 드립니다. 데이터 워크플로를 최적화하지 않을 때는, 같은 꼼꼼함으로 사진이라는 취미에 몰두합니다.
목차

말 한마디로 웹페이지를 스크래핑하세요

원하는 걸 평범한 영어로 말하면 됩니다. 아니면 아무 말도 안 해도 괜찮아요.

Thunderbit 사용해보기 무료
AI로 데이터 추출하기
Google Sheets, Airtable, Notion으로 데이터를 손쉽게 منتقل하세요
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week