DrissionPage는 Python 개발자가 오랫동안 겪어온 딜레마를 해결합니다: 빠르고 가벼운 HTTP 스크래핑과 무거운 브라우저 자동화 사이에서 하나를 선택해야 했던 문제입니다. requests로는 JavaScript 렌더링 페이지를 처리할 수 없고, Selenium/Playwright로는 단순 HTML 페이지도 브라우저를 띄워야 하니 속도와 비용이 낭비됩니다. DrissionPage는 이 두 세계를 하나의 프레임워크로 통합합니다.
주의: 이 가이드는 공개 데이터 수집만을 다룹니다. 미국 CFAA(Computer Fraud and Abuse Act)와 EU GDPR은 비공개 데이터에 대한 무단 접근을 금지합니다. robots.txt를 존중하고, 서비스 약관을 확인하며, 공식 API가 있다면 우선 사용하세요.
DrissionPage의 아키텍처: 왜 하나의 도구로 충분한가
DrissionPage는 세 가지 페이지 객체를 제공합니다. 각각의 역할과 비용 구조가 다릅니다.
SessionPage — HTTP 전용 모드
SessionPage는 Python requests를 기반으로 작동합니다. 브라우저를 띄우지 않고 HTTP 요청만 보내므로, 단순 HTML 페이지나 JSON API를 수집할 때 가장 빠르고 저렴합니다. 메모리 사용량이 적고, 초당 수백 건의 요청이 가능합니다. DrissionPage 공식 문서에 따르면, SessionPage는 requests의 모든 기능을 래핑하면서도 더 간결한 API를 제공합니다.
ChromiumPage — 브라우저 제어 모드
ChromiumPage는 Chrome DevTools Protocol(CDP)을 통해 실제 Chromium 브라우저를 제어합니다. Selenium과 달리 WebDriver를 사용하지 않고 CDP에 직접 연결하므로, 탐지 확률이 낮고 속도가 빠릅니다. JavaScript 렌더링, SPA 페이지, 동적 콘텐츠를 처리할 때 필요합니다.
WebPage — 모드 전환의 핵심
WebPage가 DrissionPage의 핵심 혁신입니다. 하나의 객체에서 page.get()으로 HTTP 모드로, page.get(mode='d')로 브라우저 모드로 전환할 수 있습니다. 전환 시 쿠키, 헤더, 세션 상태가 자동으로 공유됩니다. 예를 들어, 로그인은 HTTP 모드로 빠르게 처리하고, 로그인 후 페이지는 브라우저 모드로 렌더링하는 하이브리드 접근이 가능합니다.
이 구조가 비용을 줄이는 이유는 명확합니다. 100개 페이지 중 80개가 정적 HTML이라면, 브라우저를 띄우는 것은 20번만으로 충분합니다. Chromium 인스턴스 하나는 약 200~400MB의 메모리를 사용하므로, 80%의 요청을 HTTP로 처리하면 인프라 비용이 크게 절감됩니다.
DrissionPage 웹 스크래핑: 핵심 API 익히기
요소 탐색 — ele()와 eles()
DrissionPage의 요소 탐색 API는 직관적이면서 강력합니다. CSS 선택자, XPath, 그리고 DrissionPage만의 @ 속성 문법을 지원합니다.
from DrissionPage import WebPage
page = WebPage()
page.get('https://example.com')
# CSS 선택자
title = page.ele('css:h1').text
# 태그 + 속성 조합
inputs = page.eles('tag:input@class=search-box')
# XPath
links = page.eles('xpath://a[contains(@href, "/product/")]')
# 텍스트 기반 탐색
button = page.ele('@text()=더 보기')
ele()는 첫 번째 매치를, eles()는 모든 매치를 반환합니다. tag:input 문법은 태그 이름으로 필터링하고, @class는 속성을 지정합니다. XPath가 필요한 복잡한 탐색도 완벽히 지원됩니다.
ChromiumOptions — 브라우저 설정
from DrissionPage import ChromiumOptions, ChromiumPage
co = ChromiumOptions()
co.headless(True)
co.set_argument('--no-sandbox')
co.set_argument('--disable-gpu')
co.set_argument('--window-size=1920,1080')
co.set_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...')
page = ChromiumPage(co)
listen.start() — 백그라운드 XHR/JSON 캡처
이 기능은 DrissionPage를 다른 브라우저 자동화 도구와 차별화하는 강력한 도구입니다. listen 모듈은 브라우저의 백그라운드 네트워크 요청을 실시간으로 캡처합니다. 페이지가 AJAX로 데이터를 로드할 때, 응답 JSON을 직접 가로챌 수 있습니다.
page = ChromiumPage()
page.listen.start('api/products') # URL 패턴 필터
page.get('https://shop.example.com/catalog')
# 캡처된 패킷 대기
packet = page.listen.wait(timeout=10)
if packet:
print(packet.response.body) # JSON 응답
print(packet.url)
print(packet.method)
이 기능으로 숨겨진 API를 발견하면, 이후 수집은 SessionPage의 HTTP 모드로 전환하여 속도를 10배 이상 높일 수 있습니다. 브라우저로 페이지를 한 번 분석하고, 실제 대량 수집은 HTTP로 수행하는 전략입니다.
DrissionPage 프록시 설정: SessionPage와 ChromiumPage
DrissionPage에서 프록시를 설정하는 방법은 두 가지 모드에서 다릅니다. 각각의 메커니즘을 이해해야 합니다.
SessionPage 프록시 — set_proxies()
from DrissionPage import SessionPage
page = SessionPage()
page.set.proxies('http://user-country-US:password@gate.proxyhat.com:8080')
page.get('https://httpbin.org/ip')
print(page.response.status_code)
SessionPage의 set.proxies()는 내부적으로 requests의 proxies 파라미터를 설정합니다. HTTP/HTTPS/SOCKS5 프록시를 모두 지원합니다.
ChromiumPage 프록시 — ChromiumOptions.set_proxy()
from DrissionPage import ChromiumOptions, ChromiumPage
co = ChromiumOptions()
co.set_proxy('http://user-country-US:password@gate.proxyhat.com:8080')
page = ChromiumPage(co)
page.get('https://httpbin.org/ip')
주의: ChromiumPage는 브라우저 시작 시 프록시를 적용합니다. 런타임 중 프록시를 변경하려면 브라우저를 재시작해야 합니다. 세션 단위로 프록시를 교체하려면 새 ChromiumPage 인스턴스를 생성하세요.
왜 주거용 프록시인가
데이터센터 IP는 클라우드 제공사업자의 IP 대역으로 식별되기 쉽습니다. 많은 타겟 사이트가 프록시 탐지 기술을 사용해 데이터센터 IP를 차단합니다. 주거용(residential) 프록시는 실제 ISP에서 할당된 IP를 사용하므로, 일반 사용자의 트래픽과 구별하기 어렵습니다.
ProxyHat의 주거용 프록시는 99.9% 가동률과 전 세계 190개국 이상의 지역 타겟팅을 제공합니다. DrissionPage와 조합하면, HTTP 모드에서는 초당 50~100건의 요청을, 브라우저 모드에서는 5~10개의 동시 세션을 안정적으로 운영할 수 있습니다.
실전 예제: WebPage 하이브리드 스크래핑 with ProxyHat
이제 실제로 동작하는 완전한 예제를 살펴봅니다. WebPage를 HTTP 모드로 시작하고, 필요할 때 Chromium 모드로 에스컬레이션하는 시나리오입니다.
from DrissionPage import WebPage
import time
import random
def build_proxy_url(country='US', session_id=None, city=None):
"""ProxyHat 주거용 프록시 URL 생성"""
username = f'user-country-{country}'
if city:
username += f'-city-{city}'
if session_id:
username += f'-session-{session_id}'
return f'http://{username}:PASSWORD@gate.proxyhat.com:8080'
def scrape_with_escalation(url, target_keyword):
"""HTTP 모드로 시작, 필요시 브라우저로 에스컬레이션"""
session_id = f'sess-{random.randint(10000, 99999)}'
proxy_url = build_proxy_url(country='US', session_id=session_id)
page = WebPage()
# 1단계: HTTP 모드로 시도
page.set.proxies(proxy_url)
page.get(url, mode='s') # 's' = SessionPage 모드
# 콘텐츠 확인
content = page.html
if target_keyword in content and len(content) > 500:
print(f'[HTTP] 성공 — {len(content)} bytes')
return page.ele('css:h1').text, content
# 2단계: 브라우저 모드로 에스컬레이션
print('[HTTP] 콘텐츠 부족 — Chromium 모드로 전환')
page.get(url, mode='d') # 'd' = ChromiumPage 모드
# 쿠키는 자동으로 공유됨
time.sleep(3) # JS 렌더링 대기
rendered_content = page.html
if target_keyword in rendered_content:
print(f'[Chromium] 성공 — {len(rendered_content)} bytes')
return page.ele('css:h1').text, rendered_content
print('[실패] 두 모드 모두에서 콘텐츠를 찾지 못함')
return None, None
# 실행
result = scrape_with_escalation(
'https://example-shop.com/product/12345',
'product-details'
)
이 예제의 핵심은 비용 최적화입니다. 대부분의 페이지는 HTTP 모드로 처리되므로 브라우저 리소스를 절약하고, JS 렌더링이 필요한 페이지만 브라우저로 전환합니다. 세션 ID를 고정하면 같은 IP를 유지하여 로그인 상태가 지속됩니다.
listen으로 숨겨진 API 발견 후 HTTP 전환
from DrissionPage import ChromiumPage, SessionPage
def discover_api_and_scrape(page_url, api_pattern):
"""브라우저로 API를 발견하고 HTTP로 대량 수집"""
# 1단계: ChromiumPage로 API 발견
co = ChromiumOptions()
co.set_proxy('http://user-country-US:PASSWORD@gate.proxyhat.com:8080')
co.headless(True)
browser = ChromiumPage(co)
browser.listen.start(api_pattern)
browser.get(page_url)
packet = browser.listen.wait(timeout=15)
if not packet:
browser.close()
return None
api_url = packet.url
api_headers = packet.request.headers
print(f'[발견] API: {api_url}')
browser.close()
# 2단계: SessionPage로 대량 수집
sp = SessionPage()
sp.set.proxies('http://user-country-US-session-batch1:PASSWORD@gate.proxyhat.com:8080')
sp.set.headers(api_headers) # 발견한 헤더 재사용
results = []
for page_num in range(1, 101):
sp.get(f'{api_url}?page={page_num}')
data = sp.response.json()
results.extend(data.get('items', []))
time.sleep(random.uniform(0.5, 1.5)) # 합리적 지연
return results
이 패턴은 DrissionPage의 가장 강력한 사용 사례 중 하나입니다. 브라우저는 API를 발견하는 용도로만 사용하고, 실제 수집은 HTTP 모드에서 수행합니다. 100페이지를 수집할 때 브라우저를 100번 띄우는 대신 1번만 사용합니다.
프로덕션 패턴: 규모와 안정성
세션 단위 프록시 고정
각 작업에 고유한 세션 ID를 부여하면, 같은 작업 내에서는 동일한 IP가 유지됩니다. 이는 로그인 세션이나 다단계 폼 제출에 필수적입니다.
import uuid
def create_pinned_session(country='US'):
session_id = str(uuid.uuid4())[:8]
proxy = f'http://user-country-{country}-session-{session_id}:PASS@gate.proxyhat.com:8080'
page = WebPage()
page.set.proxies(proxy)
return page, session_id
재시도 로직과 백오프
def fetch_with_retry(page, url, max_retries=3):
for attempt in range(max_retries):
try:
resp = page.get(url, timeout=15)
if resp.status_code == 200:
return resp
elif resp.status_code == 429:
wait = 2 ** attempt + random.uniform(0, 1)
print(f'Rate limited — {wait:.1f}s 대기')
time.sleep(wait)
elif resp.status_code == 403:
print('차단됨 — 새 세션 필요')
return None
except Exception as e:
print(f'오류: {e} — 재시도 {attempt + 1}/{max_retries}')
time.sleep(2 ** attempt)
return None
동시성 관리
ChromiumPage는 메모리 집약적이므로 동시 실행 수를 제한해야 합니다. 일반적으로 메모리 4GB당 5~8개의 동시 브라우저 세션이 안정적인 임계값입니다. SessionPage는 훨씬 더 높은 동시성이 가능합니다 — 50~100개의 동시 HTTP 세션이 일반적인 범위입니다.
| 패턴 | 모드 | 동시성 권장 | 메모리/세션 | 적용 시나리오 |
|---|---|---|---|---|
| 대량 HTML 수집 | SessionPage | 50~100 | ~20MB | 정적 페이지, JSON API |
| JS 렌더링 수집 | ChromiumPage | 5~8 | 200~400MB | SPA, 동적 콘텐츠 |
| 하이브리드 | WebPage | 10~20 | 혼합 | API 발견 후 HTTP 전환 |
| API 캡처 | ChromiumPage + listen | 2~3 | 300~500MB | 숨겨진 API 탐색 |
컨테이너화 패턴
# Dockerfile 예시
FROM python:3.11-slim
RUN apt-get update && apt-get install -y \
chromium \
chromium-driver \
fonts-liberation \
&& rm -rf /var/lib/apt/lists/*
RUN pip install DrissionPage
ENV DISPLAY=:99
ENV CHROME_PATH=/usr/bin/chromium
COPY scraper.py /app/scraper.py
WORKDIR /app
CMD ["python", "scraper.py"]
컨테이너 환경에서는 --no-sandbox와 --disable-dev-shm-usage 플래그가 필수입니다. 공유 메모리가 부족하면 Chromium이 크래시할 수 있습니다.
브라우저로 에스컬레이션하지 말아야 할 때
브라우저는 만능 해결책이 아닙니다. 다음 상황에서는 HTTP 모드를 유지하세요:
- JSON API가 공개된 경우: 브라우저를 띄울 이유가 없습니다. SessionPage로 직접 호출하세요.
- 대량 페이지 수집: 1,000개 이상의 페이지를 수집할 때 브라우저는 비효율적입니다. HTTP 모드로 처리하고, 차단된 페이지만 브라우저로 재시도하세요.
- 단순 HTML 테이블: 정적 HTML에 데이터가 있다면
page.eles('tag:tr')로 충분합니다. - 속도가 최우선인 경우: 브라우저는 페이지당 2~5초가 추가됩니다. HTTP는 일반적으로 200~800ms면 충분합니다.
반대로, 다음 상황에서는 브라우저 에스컬레이션이 정당합니다:
- Cloudflare/Turnstile 챌린지: 브라우저 환경에서만 해결 가능합니다.
- 무한 스크롤: JS 이벤트를 트리거해야 데이터가 로드됩니다.
- 동적 폼 제출: JS 검증이 포함된 다단계 폼은 브라우저가 필요합니다.
- WebSocket 기반 데이터:
listen으로 캡처할 수 있습니다.
윤리와 법적 고려사항
스크래핑은 강력한 도구지만, 책임감 있게 사용해야 합니다. 핵심 원칙은 다음과 같습니다:
- 공개 데이터만 수집: 로그인 없이 접근 가능한 페이지만 대상으로 하세요. 비공개 데이터에 접근하면 CFAA 위반이 될 수 있습니다.
- robots.txt 존중: RFC 9309에 정의된 robots.txt 프로토콜을 따르세요. DrissionPage는 robots.txt를 자동으로 확인하지 않으므로, 별도로 파싱해야 합니다.
- 요청 속도 제한: 타겟 서버에 부하를 주지 않도록 초당 1~5건 이하로 유지하세요. 대량 수집 시
time.sleep(random.uniform(1, 3))을 사용하세요. - GDPR 주의: EU 사용자의 개인 데이터를 수집할 때는 GDPR 적용 대상일 수 있습니다. 개인 식별 정보(PII)는 수집하지 마세요.
- 공식 API 우선: 타겟 사이트가 공식 API를 제공한다면, 스크래핑보다 API를 사용하세요. 더 안정적이고 법적 위험이 없습니다.
ProxyHat 설정 가이드
ProxyHat과 DrissionPage의 통합은 간단합니다. 프라이싱 페이지에서 플랜을 확인하고, 위치 페이지에서 사용 가능한 국가를 확인하세요.
연결 파라미터 요약
| 파라미터 | 값 |
|---|---|
| 게이트웨이 | gate.proxyhat.com |
| HTTP 포트 | 8080 |
| SOCKS5 포트 | 1080 |
| 국가 타겟팅 | user-country-US |
| 도시 타겟팅 | user-country-DE-city-berlin |
| 세션 고정 | user-session-abc123 |
자세한 설정은 ProxyHat 문서를 참조하세요. 웹 스크래핑 사용 사례와 SERP 추적 사용 사례 페이지에서 더 많은 실전 시나리오를 확인할 수 있습니다.
핵심 요약
Key Takeaways:
- DrissionPage의 WebPage는 HTTP와 Chromium 모드를 전환하며 쿠키/세션을 공유합니다 — 80%의 요청을 HTTP로 처리해 비용을 절감하세요.
listen.start()로 숨겨진 API를 발견한 후 SessionPage로 대량 수집하는 하이브리드 패턴이 가장 효율적입니다.- 프록시는 SessionPage의
set.proxies()와 ChromiumPage의ChromiumOptions.set_proxy()로 설정합니다.- 주거용 프록시로 세션을 고정하고, 요청 간 1~3초 지연을 두어 차단을 피하세요.
- 브라우저는 JS 렌더링이 필요할 때만 사용하고, 정적 페이지는 항상 HTTP 모드를 우선하세요.
- 공개 데이터만 수집하고, robots.txt를 존중하며, 공식 API가 있으면 우선 사용하세요.
DrissionPage와 ProxyHat 주거용 프록시의 조합은 Python 스크래퍼에게 강력한 도구입니다. 하나의 프레임워크로 HTTP와 브라우저를 전환하며, 주거용 IP로 차단을 회피하고, listen으로 숨겨진 API를 발견하는 워크플로우는 다른 어떤 도구 조합으로도 쉽게 구현할 수 없는 효율성을 제공합니다. ProxyHat 프라이싱을 확인하고 지금 시작하세요.






