웹사이트가 스크래퍼를 차단하는 이유
웹사이트는 정상적인 사용자 트래픽을 원합니다. 자동화된 스크래퍼가 과도한 요청으로 서버에 부하를 주거나, 데이터를 대량 수집하거나, 유료 콘텐츠에 접근하려 할 때 차단이 발생합니다. 현대의 안티봇 시스템은 단순한 IP 기반 차단을 넘어 다층적인 탐지 메커니즘을 사용합니다.
이 가이드에서는 웹 스크래핑 시 차단을 피하는 검증된 기법을 상세히 다룹니다.
안티봇 시스템의 작동 방식
차단을 피하려면 먼저 안티봇 시스템이 무엇을 검사하는지 이해해야 합니다:
IP 기반 탐지
- 속도 제한 — 단일 IP에서 특정 시간 내 허용된 요청 수 초과 감지
- IP 평판 — IP 주소가 알려진 데이터센터, VPN, 프록시 서비스에 속하는지 확인
- 지역 이상 — 짧은 시간 내 다른 지역에서의 접근 감지
브라우저 핑거프린트 탐지
- TLS 핑거프린트 — JA3/JA4 해시로 클라이언트의 TLS 구현을 식별
- HTTP/2 핑거프린트 — 프레임 설정, 헤더 순서로 클라이언트 식별
- JavaScript 환경 — navigator, screen, WebGL 속성 검사
행동 분석
- 요청 패턴 — 순차적 URL 접근, 일정한 간격, 비정상적 탐색 경로
- 마우스/키보드 이벤트 — 인간의 상호작용 부재 감지
- 세션 행동 — 쿠키 없는 접근, 리퍼러 헤더 부재
프록시 로테이션 전략
프록시 로테이션은 차단 방지의 기본입니다. 하지만 단순히 IP를 바꾸는 것만으로는 부족합니다.
요청별 로테이션
각 요청에 새 IP를 할당하는 가장 기본적인 방식입니다. 독립적인 페이지를 대량으로 수집할 때 적합합니다:
from proxyhat import ProxyHat
client = ProxyHat(api_key="your_api_key")
urls = ["https://example.com/product/" + str(i) for i in range(1, 101)]
for url in urls:
response = client.get(url, proxy_type="residential")
if response.status_code == 200:
process(response.text)
스티키 세션
로그인, 장바구니, 다중 페이지 탐색처럼 세션 유지가 필요한 경우 동일한 IP를 일정 시간 유지합니다:
session = client.session(proxy_type="residential", sticky_ttl=600)
# 동일 IP로 다중 페이지 접근
login_page = session.get("https://example.com/login")
session.post("https://example.com/login", data={"user": "test", "pass": "test"})
dashboard = session.get("https://example.com/dashboard")
지역 분산
요청을 여러 지역에 분산하여 단일 지역에서의 집중 요청을 피합니다.
헤더 관리
적절한 HTTP 헤더는 차단 방지에 핵심적입니다.
User-Agent 로테이션
실제 브라우저의 User-Agent 문자열을 사용하고 요청마다 로테이션합니다:
import random
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
]
headers = {
"User-Agent": random.choice(user_agents),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
}
현실적인 헤더 조합
User-Agent와 함께 일관된 헤더 세트를 사용해야 합니다. Chrome User-Agent를 사용하면서 Firefox 전용 헤더를 보내면 탐지됩니다.
속도 제한 및 지연
요청 사이에 적절한 지연을 두는 것은 탐지 방지의 기본입니다:
import time
import random
def polite_scrape(urls, client):
for url in urls:
response = client.get(url, proxy_type="residential")
process(response)
# 1-3초 무작위 지연
delay = random.uniform(1.0, 3.0)
time.sleep(delay)
적응형 지연
응답 코드에 따라 지연을 조정합니다:
- 200 OK — 정상 지연 유지
- 429 Too Many Requests — 지연을 2-3배 증가
- 403 Forbidden — IP 로테이션 후 더 긴 지연으로 재시도
- 503 Service Unavailable — 잠시 일시 중지 후 재시도
JavaScript 렌더링
많은 현대 웹사이트가 JavaScript로 콘텐츠를 동적으로 로드합니다. 원시 HTTP 요청으로는 빈 페이지만 받게 됩니다.
헤드리스 브라우저 사용
Puppeteer나 Playwright로 JavaScript를 렌더링합니다:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={"server": "http://gate.proxyhat.com:8080",
"username": "user", "password": "pass"}
)
page = browser.new_page()
page.goto("https://example.com/products")
page.wait_for_selector(".product-list")
content = page.content()
browser.close()
쿠키와 세션 관리
일부 사이트는 유효한 세션 쿠키 없이는 콘텐츠를 제공하지 않습니다:
- 홈페이지를 먼저 방문하여 초기 쿠키를 받으십시오
- 스티키 세션으로 쿠키를 유지하십시오
- CSRF 토큰을 적절히 처리하십시오
고급 기법
요청 순서 무작위화
URL을 순차적으로 접근하지 말고 무작위로 섞으십시오. 다른 유형의 페이지를 혼합하여 자연스러운 탐색 패턴을 모방합니다.
리퍼러 헤더 체인
각 요청에 이전 페이지를 리퍼러로 설정하여 자연스러운 탐색 흐름을 만듭니다.
DNS-over-HTTPS
DNS 질의를 암호화하여 네트워크 수준의 모니터링을 방지합니다.
핵심 요약
- 프록시 로테이션은 기본이지만 그것만으로는 부족합니다 — 헤더, 지연, 행동 패턴도 관리해야 합니다.
- 현실적인 헤더를 사용하고 User-Agent와 다른 헤더의 일관성을 유지하십시오.
- 요청 사이에 무작위 지연을 두어 자연스러운 브라우징 패턴을 모방하십시오.
- JavaScript 렌더링이 필요한 사이트에는 헤드리스 브라우저를 사용하십시오.
- 세션 관리와 쿠키 처리를 소홀히 하지 마십시오.
- ProxyHat의 레지덴셜 프록시는 높은 성공률과 자동 IP 로테이션을 제공합니다.






