AliExpress 제품 데이터를 스크래핑하여 드롭쉬핑 연구하는 방법

AliExpress는 제품 데이터를 HTML이 아닌 JavaScript 변수에 숨깁니다. 그 JSON을 가져오면 가격, 할인, 발송지 및 판매 수량을 한 번의 요청으로 얻을 수 있습니다. 여기서는 차단 방지 방법과 함께 그 방법을 설명합니다.

AliExpress는 세계 최대의 글로벌 마켓플레이스이며 드롭쉬핑 연구의 주요 소스입니다: 가격 및 할인 기록, 실제로 판매되는 것, 발송지, 국가별로 어떻게 제안이 변하는지. 2026년에는 JavaScript 렌더링과 무작위 클래스 이름에 크게 의존하여 사람들을 전체 브라우저를 실행하게 만듭니다. 보통은 필요하지 않습니다. 제품 데이터는 이미 JavaScript 변수 안에 페이지 소스에 있습니다 — 그것을 추출하면 단일 요청으로 깨끗한 구조화된 데이터를 얻을 수 있습니다. 이 가이드는 숨겨진 JSON 방법, AliExpress 데이터를 가치 있게 만드는 지리적 차원, 스크래퍼를 유지하는 프록시 배관을 보여줍니다.

데이터는 window.runParams에 있으며, DOM에는 없습니다.

카테고리 또는 검색 결과 페이지를 열고 소스를 봅니다. 모든 제품 미리보기는 window.runParams라는 JavaScript 변수에 저장되어 있으며, <script> 태그 안에 숨겨져 있습니다. 이것은 현대적인 일반적인 패턴입니다 — 서버는 데이터를 JSON으로 전송하고 프론트 엔드는 클라이언트 측에서 렌더링합니다. 스크래퍼에게는 선물입니다: 취약한 CSS 선택자를 완전히 건너뛰고, 정규 표현식으로 스크립트 태그를 가져와 사전처럼 구문 분석합니다. 이 기술은 숨겨진 웹 데이터 스크래핑이라고 하며, 배포마다 변경되는 클래스 이름을 쫓는 것보다 훨씬 더 내구성이 있습니다.

import re, json, httpx

HEADERS = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
    "accept-language": "en-US,en;q=0.9",
}

PROXY = "http://USER:PASS@gate.quantumproxies.io:8000"

def get_run_params(html: str) -> dict:
    # The product data lives in window.runParams = {...};
    m = re.search(r"window\.runParams\s*=\s*(\{.+?\});", html, re.DOTALL)
    if not m:
        raise ValueError("runParams not found - page may be blocked or changed")
    return json.loads(m.group(1))

url = "https://www.aliexpress.com/category/.../catName.html"
r = httpx.get(url, headers=HEADERS, proxy=PROXY, timeout=30)
data = get_run_params(r.text)

연구에 중요한 필드 구문 분석

runParams 블롭은 방대하므로 드롭쉬핑 및 가격 연구에 실제로 사용하는 필드만 가져옵니다: 목록 제목, 원래 가격 대 판매 가격, 할인율, 판매된 수량, 그리고 — 결정적으로 — 발송지 국가, 이는 배송 시간과 구매자 신뢰를 좌우합니다. 중첩은 위협적으로 보이지만 안정적입니다:

def parse_items(data: dict) -> list[dict]:
    items = data.get("mods", {}).get("itemList", {}).get("content", [])
    out = []
    for it in items:
        prices = it.get("prices", {})
        sale = prices.get("salePrice", {})
        orig = prices.get("originalPrice", {})
        out.append({
            "id": it.get("productId"),
            "title": it.get("title", {}).get("displayTitle"),
            "sale_price": sale.get("minPrice"),
            "orig_price": orig.get("minPrice"),
            "discount_pct": sale.get("discount"),
            "currency": sale.get("currencyCode"),
            "sold": it.get("trade", {}).get("tradeDesc"),   # e.g. "1,000+ sold"
            "store_id": it.get("store", {}).get("storeId"),
        })
    return out

for row in parse_items(data)[:5]:
    print(row["title"][:40], row["sale_price"], f"-{row['discount_pct']}%", row["sold"])

그 한 번의 호출로 제품의 경제성을 얻을 수 있습니다: 원래 가격이 $65.85인 목록이 $23.29의 판매 가격으로 떨어지는 것은 64% 할인입니다 — 아이템을 소싱할 가치가 있는지 알려주는 마진 신호입니다. "판매된" 수량은 수요의 대리자입니다; 카테고리 전체에서 결합하면 리뷰 페이지를 보지 않고도 승자를 순위 매길 수 있습니다.

AliExpress 페이지 요청, window.runParams의 정규 표현식 추출, JSON으로 구문 분석된 제품 필드를 보여주는 파이프라인 다이어그램
목록 데이터는 JavaScript 변수로 서버에서 렌더링됩니다 — 읽기 위해 헤드리스 브라우저가 필요하지 않습니다.

사이트를 과도하게 사용하지 않고 검색 페이지네이션

검색 페이지는 알려진 길이의 페이지네이션을 사용하므로 효율적인 관용구는 다음과 같습니다: 첫 페이지를 스크래핑하고, 응답에서 총 페이지 수를 읽은 다음 제한 내에서 나머지를 동시에 가져옵니다. 모든 페이지를 한 번에 발사하지 마십시오 — AliExpress는 공격적으로 속도 제한을 하고 403을 반환하기 시작할 것입니다. 동시성을 적당히 유지하고 각 요청에 대해 종료 IP를 회전하십시오:

import asyncio, httpx

ROTATING = "http://USER:PASS@rotating.quantumproxies.io:8000"

async def fetch_page(client, query, page):
    url = f"https://www.aliexpress.com/w/wholesale-{query}.html?page={page}"
    for attempt in range(3):
        r = await client.get(url, headers=HEADERS, timeout=30)
        if r.status_code == 200:
            return parse_items(get_run_params(r.text))
        if r.status_code == 403:                 # blocked: back off, rotate exit
            await asyncio.sleep(2 ** attempt)
    return []

async def scrape_search(query, max_pages=5):
    async with httpx.AsyncClient(proxy=ROTATING) as client:
        sem = asyncio.Semaphore(3)               # gentle concurrency
        async def guarded(p):
            async with sem:
                return await fetch_page(client, query, p)
        pages = await asyncio.gather(*[guarded(p) for p in range(1, max_pages + 1)])
        return [row for page in pages for row in page]

results = asyncio.run(scrape_search("wireless earbuds"))
print(len(results), "products")

여기서 회전 게이트웨이가 중요합니다: 한 IP가 페이지를 계속 가져오는 것은 차단을 받는 가장 빠른 방법이며, 요청당 회전은 주거용 풀을 통해 부하를 분산하여 어떤 종료도 비정상적으로 보이지 않게 합니다. 403에 대한 지수적 백오프는 소진된 IP가 순환할 시간을 줍니다.

90M+ 주거용 IP로 AliExpress 스크래핑

지리가 핵심입니다

AliExpress는 방문자의 위치에 따라 가격, 통화, 프로모션 및 발송지 창고를 개인화합니다 — 동일한 제품이 미국 IP에는 더 빠른 배송으로 미국 현지 카드로 표시되고, 유럽 IP에는 다른 가격과 출처로 표시될 수 있습니다. 드롭쉬핑 연구에서는 그 지리적 차원이 데이터입니다: 목표 시장의 고객이 실제로 무엇을 보는지 알고 싶습니다. 판매하는 각 시장에 IP를 통해 경로를 설정하고 차이를 기록하십시오; 한 지역에서는 저렴하고 현지에서 배송되지만 다른 지역에서는 느리고 비싼 제품은 매우 다른 선택입니다. 드롭쉬핑 연구를 위한 프록시에 대한 우리의 가이드에서는 이러한 행렬을 소싱 결정으로 전환하는 방법을 더 깊이 다룹니다.

# One product, three markets - compare price and ship-from per geo
MARKETS = {
    "us": "http://USER:PASS-country-us@gate.quantumproxies.io:8000",
    "de": "http://USER:PASS-country-de@gate.quantumproxies.io:8000",
    "br": "http://USER:PASS-country-br@gate.quantumproxies.io:8000",
}
for market, proxy in MARKETS.items():
    r = httpx.get(product_url, headers=HEADERS, proxy=proxy, timeout=30)
    d = get_run_params(r.text)
    # read price + shipFrom from the product detail runParams for this geo
    print(market, "->", extract_price_and_origin(d))

리뷰와 재고는 별도의 호출에 있습니다

목록은 가격과 수요를 제공합니다, 하지만 두 가지 고가치 필드는 자체 요청 뒤에 있습니다. 고객 리뷰는 제품 페이지의 runParams에 있지 않습니다 — 전용 피드백 엔드포인트에서 로드되며, 페이지네이션되어 있어, 평점과 리뷰 텍스트를 수집하려면 메인 페이지를 구문 분석하는 대신 제품당 해당 호출을 따라야 합니다. 재고 신호도 유사합니다: 목록의 "판매된" 수량은 누적 수요의 대리자이며, 변형(색상, 크기, 발송지 창고)별 정확한 가용성은 제품 세부 정보 페이로드의 SKU 데이터에서 나옵니다. 드롭쉬핑 연구에서는 이것이 중요합니다. 왜냐하면 제품이 가격과 판매에서 승자처럼 보일 수 있지만, 판매하려는 특정 변형이 시장에 서비스하는 창고에서 재고가 없을 수 있기 때문입니다.

두 가지를 두 번째 패스로 처리하십시오: 목록을 먼저 스크래핑하여 할인 및 판매 수량으로 후보를 순위 매기고, 그런 다음 리뷰 페이지 및 변형별 재고로 짧은 목록만 풍부하게 만드십시오. 그렇게 하면 요청 볼륨 — 그리고 차단 위험 — 이 각 제품에 대해 얼마나 진지한지에 비례하게 유지되며, 사용하지 않을 데이터를 위해 모든 목록을 과도하게 사용하지 않습니다.

DIY 스크래퍼를 건너뛸 때

숨겨진 JSON 방법은 내구성이 있지만, AliExpress는 페이로드 모양을 변경하고 리뷰를 별도의 엔드포인트 뒤에 게이트하고, 볼륨에 따라 차단을 강화합니다. 정규 표현식 및 재시도 로직을 유지하고 싶지 않다면, Scraper API는 URL을 받아 구문 분석된 JSON을 반환합니다 — 렌더링, 회전 및 차단 방지 레이어를 처리하며, 단일 매개변수로 지리적 타겟팅을 할 수 있습니다. 여러 시장에서 수천 개의 제품을 일정에 따라 수집할 때 실용적인 선택입니다. 클라이언트 측 페이로드에서 데이터를 직접 읽는 일반적인 패턴에 대해서는 스크래퍼가 빈 페이지를 반환하는 이유에 대한 우리의 노트를 참조하십시오.

403 및 지리적 가격과 같은 AliExpress 스크래핑 장애물을 주거용 회전 및 지리적 타겟팅과 같은 해결책에 매핑하는 체크리스트
세 가지 장애물, 세 가지 해결책 — 대부분의 AliExpress 스크래퍼 실패는 IP 품질 또는 지리적 문제입니다.

자주 묻는 질문

AliExpress 제품 데이터를 스크래핑할 수 있습니까?

예. 공개적으로 볼 수 있는 목록 데이터 — 제목, 가격, 할인, 판매 수량, 이미지 및 발송지 — 는 카테고리, 검색 및 제품 페이지의 window.runParams JavaScript 변수로 제공됩니다. 정규 표현식으로 추출하고 JSON으로 구문 분석하여 목록에 대해 헤드리스 브라우저가 필요하지 않습니다. AliExpress의 약관과 속도 제한을 준수하십시오.

AliExpress에 공개 API가 있습니까?

AliExpress는 제휴 및 오픈 플랫폼 API를 제공하지만 승인이 필요하고, 범위가 제한적이며 프로그램에 연결되어 있습니다. 카테고리 및 시장 전반에 걸친 광범위한 연구를 위해서는 공개 페이지를 스크래핑하는 것이 더 많은 필드와 완전한 지리적 제어를 제공하므로 대부분의 드롭쉬핑 연구자들은 페이지의 JSON을 추출합니다.

AliExpress를 스크래핑할 때 403 오류가 발생하는 이유는 무엇입니까?

403은 AliExpress가 요청을 플래그했다는 의미입니다 — 보통 한 IP에서 너무 많은 히트, 데이터 센터 범위 또는 얇은 헤더 세트 때문입니다. 요청당 주거용 IP를 회전하고, 403에 지수적 백오프를 추가하고, 현실적인 User-Agent 및 수락 언어를 보내고, 동시성을 낮게 유지하십시오. HTML에서 runParams가 누락되면 레이아웃 변경이 아니라 차단입니다.

국가별 AliExpress 가격을 얻으려면 어떻게 해야 합니까?

대상 국가의 종료 IP를 통해 요청을 라우팅하십시오. AliExpress는 위치를 읽어 통화, 가격, 프로모션 및 발송지 창고를 설정하므로, 미국 IP와 독일 IP는 동일한 제품에 대해 다른 데이터를 봅니다. 시장별로 프록시를 지리적으로 타겟팅하고 각 버전을 기록하여 지리별 가격 행렬을 구축하십시오.

승리하는 접근 방식은 지루하고 내구성이 있습니다: 페이지가 이미 전송한 JSON을 읽고, 정중하게 페이지를 넘기고, 깨끗한 주거용 IP를 회전시키고, 관심 있는 모든 시장을 지리적으로 타겟팅하십시오. 그렇게 하면 AliExpress는 403의 벽이 아닌 가격, 수요 신호 및 배송 경제의 신뢰할 수 있는 피드가 됩니다.

Scraper API에서 구문 분석된 AliExpress 데이터 가져오기