← 블로그로 돌아가기

로컬에서 작동하는 파서, 서버에서 차단되는 이유 7가지 및 해결 방법

Wildberries, Ozon 또는 Avito 파서가 노트북에서는 완벽하게 작동하지만 서버에서는 지속적으로 차단되나요? 7가지 기술적 원인을 분석하고 코드와 인프라에서 변경해야 할 사항을 보여줍니다.

📅2026년 9월 28일

전형적인 상황: Wildberries 또는 Ozon의 가격 모니터링 스크립트가 집의 노트북에서 잘 작동하지만 VPS로 이동한 후 403, CAPTCHA 또는 IP 차단을 받기 시작합니다. 개발자는 헤더를 변경하고 지연을 추가하지만 결과는 변하지 않습니다. 문제는 거의 항상 파서 코드 자체가 아니라 요청을 수행하는 환경에 있습니다. 파서가 서버에서 안정적으로 작동하도록 변경해야 할 7가지 구체적인 이유를 분석합니다.

로컬에서는 잘 작동하는데 서버에서는 차단되는 이유

집의 컴퓨터에서 파서를 실행하면 사이트는 귀하의 ISP에서 제공하는 일반 주거용 IP 주소에서 요청을 감지합니다. Selenium 또는 Playwright를 사용하여 실제 브라우저 환경에서 요청을 보낼 경우 더욱 그렇습니다. 동일한 스크립트가 독일, 네덜란드 또는 미국의 VPS로 이동하면 상황이 완전히 바뀝니다: IP는 데이터 센터에 속하고, TLS 지문은 라이브러리의 다른 버전으로 인해 다를 수 있으며, 서버의 시간대는 IP의 지리적 위치와 일치하지 않고, 요청 빈도는 급격히 증가합니다. 서버는 24시간 연중무휴로 작동하기 때문입니다.

Wildberries, Ozon, Avito 및 대부분의 대형 마켓플레이스의 안티봇 시스템은 더 이상 User-Agent만을 확인하지 않습니다. 그들은 IP 유형, 요청 속도 및 규칙성, 페이지에서의 행동, 헤더 및 TLS 매개변수의 일치, 쿠키 및 세션 기록 등 수십 가지 신호의 조합을 분석합니다. 로컬 머신은 대부분의 항목에서 우연히 검사를 통과하지만, 서버는 거의 모든 항목에서 실패합니다. 아래는 각 이유에 대한 자세한 분석입니다.

이유 1: 데이터 센터의 IP 대신 주거용 IP

이는 80%의 경우에서 가장 큰 이유입니다. VPS 및 클라우드 서버(AWS, DigitalOcean, Hetzner, 일반 VDS 호스팅)의 IP 주소는 데이터 센터의 데이터베이스에 있습니다. 이러한 공급자의 ASN은 공개적으로 알려져 있으며, 안티봇 시스템에서 트래픽을 즉시 필터링하는 데 사용됩니다. 마켓플레이스는 자동화된 파싱의 95%가 서버 IP에서 발생하기 때문에 이러한 목록을 우선적으로 사용합니다.

해결책은 일반 인터넷 사용자와 시각적으로 구별되지 않는 IP를 사용하는 것입니다. Wildberries, Ozon 및 Avito를 파싱할 때 가장 적합한 것은 주거용 프록시입니다. 이는 일반 가입자에게 제공되는 실제 IP 주소입니다. 안티봇 시스템은 이러한 요청을 데이터 센터의 서버가 아닌 실제 사용자의 트래픽으로 인식하여 대부분의 차단을 즉시 해제합니다.

이유 2: IP 회전 및 요청 빈도 제한 없음

로컬 머신에서는 테스트 중에 수동으로 20-50개의 요청을 수행하며, 사이트는 이를 감지하지 않습니다. 서버에서는 스크립트가 매 5분마다 cron으로 실행되며, 하나의 IP에서 연속적으로 수천 개의 상품 카드를 처리합니다. 이러한 패턴은 안티봇 시스템에 대한 직접적인 신호입니다: 실제 사람은 한 시간 안에 단 한 번의 휴식 없이 3000개의 카탈로그 페이지를 열 수 없습니다.

IP 풀에서 회전을 도입하고 특정 시간 내에 하나의 주소에 대한 요청 수를 제한해야 합니다. 실용적인 규칙: 상품 카드에 대해 하나의 IP에서 분당 30-60개의 요청을 초과하지 않으며, 요청 묶음 후 자동으로 주소를 변경합니다. Python에서 프록시 풀을 통한 회전 설정 예시:

import requests

proxies_pool = [
    "http://user:[email protected]:9000",
    "http://user:[email protected]:9001",
    "http://user:[email protected]:9002",
]

def get_page(url, session_id):
    proxy = proxies_pool[session_id % len(proxies_pool)]
    resp = requests.get(
        url,
        proxies={"http": proxy, "https": proxy},
        headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"},
        timeout=10
    )
    return resp.text

하루에 많은 수의 카드를 수집할 때는 요청 시 자동으로 IP를 회전하는 프록시를 사용하는 것이 더 편리합니다. 이렇게 하면 주소 목록을 수동으로 유지할 필요가 없습니다.

이유 3: 헤더와 User-Agent가 브라우저와 유사하지 않음

많은 파서가 requests 또는 aiohttp를 사용하여 최소한의 헤더 세트 또는 라이브러리의 기본 User-Agent로 요청을 보냅니다. 이는 즉시 스크립트를 드러냅니다(예: python-requests/2.31.0). 로컬 머신에서는 브라우저를 통해 헤더 세트가 완전합니다: Accept, Accept-Language, Accept-Encoding, Sec-Ch-Ua, Referer 등 — 이들의 조합은 자연스럽게 보입니다.

실제 브라우저의 전체 헤더 세트를 복사해야 하며, 전송 순서도 포함해야 합니다. 일부 안티봇 시스템은 이것조차도 검사합니다. 또한 TLS 지문 버전과 동기화하여 User-Agent를 회전하는 것이 중요합니다(다음 항목 참조). 그렇지 않으면 브라우저 헤더와 실제 TLS 클라이언트 간의 불일치가 새로운 봇 신호가 될 수 있습니다.

이유 4: TLS/JA3 지문이 스크립트를 나타냄

이는 덜 알려져 있지만 서버에서 차단되는 매우 일반적인 이유입니다. requests, urllib, aiohttp 라이브러리는 Chrome이나 Firefox의 TLS 핸드셰이크 구현과 다릅니다. 안티봇 시스템은 TLS 연결의 JA3/JA4 지문을 계산하며, Python 스크립트의 지문은 실제 브라우저의 지문과 전혀 다릅니다. 헤더가 완벽하게 복사되더라도 마찬가지입니다.

해결책은 브라우저의 TLS 지문을 에뮬레이트하는 라이브러리를 사용하는 것입니다(예: Python의 curl_cffi, tls-client 또는 Playwright/Puppeteer 기반의 완전한 헤드리스 브라우저). 두 번째 옵션은 순수 HTTP 클라이언트를 사용하지 않고 안티디텍트 도구와 함께 관리되는 브라우저 엔진을 사용하는 것입니다. 이 경우 TLS와 헤더는 실제 브라우저 엔진에 의해 형성됩니다.

이유 5: 시간대, 로케일 및 DNS 서버

스크립트가 Selenium 또는 Playwright를 통해 브라우저를 에뮬레이트하는 경우, 안티봇 시스템은 시스템 시간대, 인터페이스 언어, DNS 리졸버 및 실제 서버의 IP 유출을 WebRTC를 통해 확인할 수 있습니다. 프랑크푸르트의 데이터 센터에 있는 VPS가 UTC 시스템 시간대와 호스팅 제공자의 DNS를 사용하고, 동시에 모스크바의 IP를 가진 프록시를 사용하는 경우, 지리적 데이터의 명백한 불일치가 발생합니다. 이는 탐지의 가장 신뢰할 수 있는 신호 중 하나입니다.

모든 환경 매개변수 — 시간대, 브라우저 언어, DNS, WebRTC에 의한 지리적 위치 — 는 요청에 사용되는 IP 주소의 지역과 일치해야 합니다. 이러한 문제를 해결하기 위해 안티디텍트 브라우저가 개발되었습니다: Dolphin Anty, AdsPower, Multilogin, Octo Browser 및 GoLogin은 각 프록시에 대해 별도의 "브라우저 프로필"을 설정할 수 있게 해주며, 이 프로필은 IP의 지리적 위치에 맞게 시간대, 로케일, 화면 해상도 및 WebRTC를 자동으로 조정합니다.

이유 6: 요청 패턴이 너무 "로봇화"됨

사람은 다양한 간격으로 카탈로그를 스크롤하고, 무작위 상품을 클릭하며, 가끔 뒤로 돌아가고, 페이지를 불규칙하게 스크롤합니다. 서버 스크립트는 일반적으로 동일한 간격(예: 정확히 2초마다)으로 요청을 수행하고, 필요한 URL에만 접근하며 "잡음" 없이 작동합니다 — 이미지, 스크립트 로드 없이, 상품 카드에 접근하기 전에 메인 페이지를 방문하지 않습니다.

변경해야 할 사항: 무작위 지연 추가(고정된 2초가 아니라 1.5초에서 6초 사이의 랜덤), 중간 페이지 방문(카테고리 → 카드, API에 대한 직접 요청이 아님), 헤드리스 브라우저를 통해 스크롤 및 마우스 움직임 에뮬레이션. 이는 데이터 수집 시간을 늘리지만 차단 수를 급격히 줄입니다.

이유 7: 세션과 쿠키가 요청 간에 저장되지 않음

종종 서버의 파서는 각 요청에 대해 새로운 requests 세션을 생성합니다 — 쿠키 없이, 저장된 인증 토큰 없이, 방문 기록 없이. Wildberries 및 Ozon과 같은 마켓플레이스는 첫 방문 시 임시 쿠키와 토큰을 발급하며, 이후 요청은 이를 없이 수행하면 의심스럽게 보입니다. 마치 각 요청이 새로운 익명의 방문자가 수행하는 것처럼 보입니다.

올바른 구성: 하나의 세션(requests.Session() 또는 브라우저 컨텍스트) — 하나의 IP에 대해 프록시 풀에서, 이 IP에 대한 요청 시 쿠키를 유지합니다. 프록시를 변경할 때는 새로운 세션을 시작하고 깨끗한 쿠키를 사용하여 새로운 사용자를 에뮬레이트해야 하며, 새로운 IP와 함께 이전 쿠키를 계속 사용하는 것은 불일치를 초래하고 차단을 유발합니다.

체크리스트: 순서대로 변경해야 할 사항

파서가 서버에서 지속적으로 차단되지만 로컬에서는 작동하는 경우, 이 순서대로 변경 사항을 확인하십시오 — 이렇게 하면 더 빨리 원인을 찾을 수 있습니다:

단계 확인할 사항 변경할 사항
1 서버 IP 유형 직접 호스팅 IP 대신 주거용 프록시로 전환
2 요청 빈도 IP 회전 및 주소에 대한 요청 수 제한 도입
3 요청 헤더 실제 브라우저의 전체 헤더 세트를 복사
4 TLS 지문 순수 requests 대신 curl_cffi / 헤드리스 브라우저 사용
5 시간대 및 로케일 IP 지역에 맞게 Dolphin Anty / AdsPower에서 프로필 설정
6 행동 패턴 지연 랜덤화, 중간 페이지 추가
7 세션 및 쿠키 하나의 IP에 대해 전체 요청 사이클에 하나의 세션 연결

Wildberries 및 Ozon의 카탈로그를 고빈도로 파싱할 때, 수천 페이지를 탐색하는 속도가 중요하므로 두 가지 유형의 프록시를 조합하는 경우가 많습니다: 데이터 센터 프록시는 기술적 요청(접근 가능성 확인, 상태 코드)용으로 사용하고, 주거용 프록시는 상품 카드에서 데이터를 최종적으로 수집할 때 사용합니다. 마켓플레이스 및 Avito의 모바일 애플리케이션의 경우, 모바일 프록시가 더 효과적일 수 있습니다. 이는 ASN에 따라 자동 차단 목록에 덜 걸리기 때문입니다.

결론

서버에서 파서가 차단되는 것은 로컬 버전이 작동할 때 거의 항상 스크립트의 논리가 아니라 환경과 관련이 있습니다: IP 유형, TLS 지문, 헤더, 시간대, 요청 패턴 및 세션 관리. 가장 일반적인 이유(IP 데이터 센터)에서 가장 미세한 이유(시간대와 IP 지역 불일치)까지 7가지 이유를 순서대로 점검하면 데이터 수집의 주요 비즈니스 논리를 변경하지 않고도 파서의 안정적인 작동을 복원할 수 있습니다.

Wildberries, Ozon 또는 Avito에서 가격 및 재고를 대량으로 수집하는 경우, IP를 교체하는 것부터 시작하십시오: 표준 VPS 주소 대신 주거용 프록시를 사용해 보십시오. 대부분의 경우, 이는 헤더 및 TLS 지문을 설정하기 전에 70%의 차단을 제거합니다.