프록시를 통한 Cloudflare 감지를 우회하는 7가지 검증된 방법
Cloudflare는 전체 웹 트래픽의 20% 이상을 처리하며, 다층 봇 방지 시스템을 사용합니다. 프록시 서버를 사용할 경우 캡차나 차단을 받을 확률이 크게 증가합니다. 이 가이드에서는 2024년에 작동하는 감지 기술적 측면과 우회 방법을 살펴보겠습니다.
Cloudflare가 프록시와 봇을 식별하는 방법
Cloudflare는 각 요청의 수십 가지 매개변수를 검사하는 복합 분석 시스템을 사용합니다. 감지 메커니즘을 이해하는 것은 보호 우회의 첫 번째 단계입니다.
주요 감지 방법
TLS 지문: Cloudflare는 SSL/TLS 핸드셰이크의 매개변수(암호 스위트, 확장, 순서)를 분석합니다. 각 HTTP 클라이언트는 고유한 "지문"을 가지고 있습니다. 예를 들어, Python requests는 Chrome이나 Firefox와 쉽게 구별되는 고유한 암호 세트를 사용하는 OpenSSL을 사용합니다.
Cloudflare는 요청을 분석할 때 TLS 지문을 명시된 User-Agent와 비교합니다. Chrome 120을 지정했지만 TLS 매개변수가 Python requests에 해당하면 즉시 봇으로 감지됩니다.
| 검사 매개변수 | 분석되는 항목 | 감지 위험 |
|---|---|---|
| TLS 지문 | 암호 스위트, 확장, TLS 버전 | 높음 |
| HTTP/2 지문 | 헤더 순서, SETTINGS 프레임 | 높음 |
| IP 평판 | IP 이력, 데이터 센터 소속 | 중간 |
| JavaScript 챌린지 | JS 실행, 캔버스 지문, WebGL | 높음 |
| 행동 분석 | 요청 패턴, 타이밍, 마우스 움직임 | 중간 |
2023년부터 Cloudflare는 행동 패턴 분석을 위해 머신 러닝을 적극적으로 사용하고 있습니다. 이 시스템은 기술적 매개변수뿐만 아니라 요청 간 시간 간격, 사용자 행동 순서, 마우스 움직임 및 페이지 스크롤을 추적합니다.
TLS 지문 숨기기
TLS 지문 분석은 봇 감지의 가장 효과적인 방법입니다. 표준 HTTP 클라이언트(requests, curl, axios)는 실제 브라우저와 혼동할 수 없는 지문을 생성합니다. 해결책은 브라우저의 TLS 동작을 모방하는 전문 라이브러리를 사용하는 것입니다.
curl-impersonate 사용하기
curl-impersonate 라이브러리는 인기 있는 브라우저의 TLS 및 HTTP/2 지문을 정확하게 복제하는 수정된 curl 버전입니다. Chrome, Firefox, Safari 및 Edge를 지원합니다.
# curl-impersonate 설치
git clone https://github.com/lwthiker/curl-impersonate
cd curl-impersonate
make chrome-build
# Chrome 120으로 모방하여 사용하기
curl_chrome120 -x http://username:password@proxy.example.com:8080 \
-H "Accept-Language: en-US,en;q=0.9" \
https://example.com
Python: tls-client 라이브러리
Python에는 curl-impersonate를 내부적으로 사용하는 tls-client 래퍼가 있습니다. 이 라이브러리는 requests와 유사한 인터페이스를 제공합니다.
import tls_client
# Chrome 120 지문으로 세션 생성
session = tls_client.Session(
client_identifier="chrome_120",
random_tls_extension_order=True
)
# 프록시 설정
proxies = {
'http': 'http://username:password@proxy.example.com:8080',
'https': 'http://username:password@proxy.example.com:8080'
}
# 요청 실행
response = session.get(
'https://example.com',
proxies=proxies,
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
}
)
print(response.status_code)
중요: tls-client를 사용할 때는 헤더의 User-Agent가 선택한 client_identifier와 일치해야 합니다. 불일치는 즉각적인 감지로 이어질 수 있습니다.
TLS 지문 확인하기
파싱을 시작하기 전에 TLS 지문을 확인하는 것이 좋습니다. tls.peet.ws 또는 ja3er.com와 같은 서비스를 사용하여 분석하세요.
# 지문 확인하기
response = session.get('https://tls.peet.ws/api/all')
print(response.json()['tls']['ja3'])
# 실제 Chrome의 지문과 비교하세요:
# https://kawayiyi.com/tls-fingerprint-database/
HTTP 헤더 올바르게 설정하기
올바른 TLS 지문이 있더라도 잘못된 HTTP 헤더는 봇으로 감지될 수 있습니다. Cloudflare는 헤더의 존재뿐만 아니라 순서, 값의 형식 및 논리적 일관성도 분석합니다.
Chrome을 위한 필수 헤더
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"',
'Cache-Control': 'max-age=0'
}
Sec-Ch-Ua-* 헤더는 Chrome 89에서 등장했으며 Client Hints API의 일부입니다. 최신 User-Agent를 사용할 때 이 헤더가 없으면 명백한 봇의 징후입니다.
헤더 순서가 중요하다
HTTP/2에서는 각 브라우저에 대해 헤더 순서가 고정되어 있습니다. Python requests 및 기타 표준 클라이언트는 헤더를 알파벳 순서로 전송하는데, 이는 브라우저의 동작과 다릅니다. 사용자 정의 헤더 순서를 지원하는 라이브러리를 사용하세요.
팁: DevTools 브라우저(네트워크 탭 → 요청에서 오른쪽 클릭 → 복사 → cURL로 복사)를 사용하여 실제 브라우저의 헤더를 정확하게 복사하세요. 그런 다음 코드를 조정하세요.
User-Agent 동적 생성
모든 요청에 동일한 User-Agent를 사용하는 것은 감지 위험을 증가시킵니다. 최신 User-Agent 풀을 생성하고 이를 회전시키세요.
import random
# 최신 User-Agent 풀 (2024년 12월)
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',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
]
def get_random_headers():
ua = random.choice(USER_AGENTS)
# 선택한 UA에 맞게 다른 헤더 조정
if 'Chrome' in ua:
return {
'User-Agent': ua,
'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120"',
# ... 나머지 Chrome 헤더
}
elif 'Firefox' in ua:
return {
'User-Agent': ua,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# ... Firefox 헤더
}
# ... 다른 브라우저 처리
헤드리스 브라우저 사용하기
Cloudflare가 JavaScript 챌린지 또는 고급 감지를 사용할 때, 신뢰할 수 있는 유일한 방법은 실제 브라우저입니다. 헤드리스 브라우저는 JavaScript, 쿠키를 자동으로 처리하고 완전히 인증된 지문을 생성합니다.
anti-detect 패치가 있는 Playwright
Playwright는 Selenium의 현대적인 대안으로 더 나은 성능을 제공합니다. 그러나 표준 Playwright는 navigator.webdriver 및 기타 마커를 통해 쉽게 감지됩니다. 마스킹을 위해 playwright-stealth를 사용하세요.
from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
def bypass_cloudflare(url, proxy):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
proxy={
"server": f"http://{proxy['host']}:{proxy['port']}",
"username": proxy['username'],
"password": proxy['password']
},
args=[
'--disable-blink-features=AutomationControlled',
'--disable-dev-shm-usage',
'--no-sandbox'
]
)
context = browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
locale='en-US',
timezone_id='America/New_York'
)
page = context.new_page()
stealth_sync(page) # anti-detect 패치 적용
# 페이지로 이동
page.goto(url, wait_until='networkidle', timeout=30000)
# Cloudflare 챌린지를 통과할 때까지 대기 (보통 5-10초)
page.wait_for_timeout(8000)
# 성공적으로 우회했는지 확인
if 'Just a moment' in page.content():
print('Cloudflare 챌린지를 통과하지 못했습니다.')
return None
# 이후 사용을 위한 쿠키 추출
cookies = context.cookies()
html = page.content()
browser.close()
return {'html': html, 'cookies': cookies}
# 사용 예
proxy_config = {
'host': 'proxy.example.com',
'port': 8080,
'username': 'user',
'password': 'pass'
}
result = bypass_cloudflare('https://example.com', proxy_config)
Puppeteer Extra와 플러그인
Node.js 생태계에서 가장 좋은 솔루션은 puppeteer-extra와 puppeteer-extra-plugin-stealth 플러그인입니다. 이 플러그인은 30가지 이상의 다양한 자동화 마스킹 기법을 적용합니다.
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
async function bypassCloudflare(url, proxyUrl) {
const browser = await puppeteer.launch({
headless: 'new',
args: [
`--proxy-server=${proxyUrl}`,
'--disable-blink-features=AutomationControlled',
'--window-size=1920,1080'
]
});
const page = await browser.newPage();
// 뷰포트 및 user-agent 설정
await page.setViewport({ width: 1920, height: 1080 });
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
// navigator.webdriver 재정의
await page.evaluateOnNewDocument(() => {
delete Object.getPrototypeOf(navigator).webdriver;
});
// 페이지로 이동
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
// 챌린지 통과 대기
await page.waitForTimeout(8000);
// 콘텐츠 및 쿠키 가져오기
const content = await page.content();
const cookies = await page.cookies();
await browser.close();
return { content, cookies };
}
// 사용 예
bypassCloudflare('https://example.com', 'http://user:pass@proxy.example.com:8080')
.then(result => console.log('성공'))
.catch(err => console.error(err));
성능: 헤드리스 브라우저는 인스턴스당 200-500MB의 RAM을 소비합니다. 고부하 작업에는 쿠키를 얻기 위해서만 사용하고, 이후에는 HTTP 클라이언트로 전환하세요.
Cloudflare 우회를 위한 프록시 유형 선택하기
프록시 유형은 우회의 성공에 중요한 영향을 미칩니다. Cloudflare는 데이터 센터의 IP 주소 데이터베이스를 유지하며, 이에 대해 더 엄격한 검사 규칙을 적용합니다.
| 프록시 유형 | 우회 가능성 | 속도 | 비용 | 추천 |
|---|---|---|---|---|
| 데이터 센터 | 30-40% | 높음 | 낮음 | 헤드리스 브라우저와 함께만 사용 |
| 주거용 | 85-95% | 중간 | 높음 | 최적의 선택 |
| 모바일 | 90-98% | 중간 | 매우 높음 | 중요한 작업에 적합 |
| ISP (정적 주거용) | 80-90% | 높음 | 중간 | 가격과 품질의 균형 |
주거용 프록시가 더 효과적인 이유
주거용 프록시는 실제 장치(가정용 라우터, 스마트폰)의 IP 주소를 사용합니다. Cloudflare는 이러한 IP를 대량으로 차단할 수 없으며, 이는 일반 사용자도 차단하게 됩니다. 통계에 따르면 주거용 IP는 데이터 센터보다 캡차를 15-20배 덜 받습니다.
주거용 프록시를 사용할 때는 지리적 위치가 매우 중요합니다. 대상 사이트가 미국을 겨냥하고 있다면 아시아의 프록시를 사용하면 의심을 받을 수 있습니다. 도시별 타겟팅이 가능한 폭넓은 지리적 범위를 가진 공급자를 선택하세요.
최대 신뢰성을 위한 모바일 프록시
모바일 프록시는 모바일 통신사의 IP 주소(4G/5G)를 사용합니다. 모바일 네트워크의 특징은 비행기 모드를 통해 IP가 동적으로 변경되어 거의 무한한 수의 깨끗한 IP 주소를 제공합니다. 모바일 IP의 차단 확률은 거의 제로에 가깝습니다.
# API를 통한 모바일 IP 회전 예제
import requests
import time
def rotate_mobile_ip(proxy_api_url):
"""모바일 프록시의 IP 변경"""
response = requests.get(f"{proxy_api_url}/rotate")
if response.status_code == 200:
print("IP가 성공적으로 변경되었습니다.")
time.sleep(5) # 변경 적용 대기
return True
return False
# 모바일 프록시 사용
mobile_proxy = "http://user:pass@mobile.proxy.com:8080"
for i in range(10):
# 요청 실행
response = requests.get(
'https://example.com',
proxies={'http': mobile_proxy, 'https': mobile_proxy}
)
# 각 요청 후 IP 회전
rotate_mobile_ip('https://api.proxy.com/mobile')
쿠키 및 세션 관리하기
Cloudflare 챌린지를 성공적으로 통과한 후, 서버는 클라이언트의 합법성을 확인하는 쿠키(cf_clearance, __cfduid 등)를 설정합니다. 이러한 쿠키를 올바르게 관리하면 반복 검사를 피할 수 있습니다.
cf_clearance 추출 및 재사용
cf_clearance 쿠키는 일반적으로 30-60분 동안 유효합니다. 헤드리스 브라우저를 통해 얻은 후, 일반 HTTP 요청에서 사용할 수 있습니다.
import requests
import pickle
from datetime import datetime, timedelta
class CloudflareCookieManager:
def __init__(self, cookie_file='cf_cookies.pkl'):
self.cookie_file = cookie_file
self.cookies = self.load_cookies()
def load_cookies(self):
"""저장된 쿠키 로드"""
try:
with open(self.cookie_file, 'rb') as f:
data = pickle.load(f)
# 유효 기간 확인
if data['expires'] > datetime.now():
return data['cookies']
except FileNotFoundError:
pass
return None
def save_cookies(self, cookies, ttl_minutes=30):
"""TTL과 함께 쿠키 저장"""
data = {
'cookies': cookies,
'expires': datetime.now() + timedelta(minutes=ttl_minutes)
}
with open(self.cookie_file, 'wb') as f:
pickle.dump(data, f)
def get_cf_clearance(self, url, proxy):
"""브라우저를 통해 cf_clearance 얻기"""
if self.cookies:
return self.cookies
# 여기서 브라우저 실행 코드 (이전 섹션에서)
# ...
browser_cookies = bypass_cloudflare(url, proxy)['cookies']
# requests 형식으로 변환
cookies_dict = {c['name']: c['value'] for c in browser_cookies}
self.save_cookies(cookies_dict)
self.cookies = cookies_dict
return cookies_dict
def make_request(self, url, proxy):
"""쿠키 자동 관리와 함께 요청하기"""
cookies = self.get_cf_clearance(url, proxy)
response = requests.get(
url,
cookies=cookies,
proxies={'http': proxy, 'https': proxy},
headers=get_random_headers()
)
# 챌린지를 다시 받으면 쿠키를 갱신
if response.status_code == 403 or 'cf-browser-verification' in response.text:
print("쿠키가 만료되었습니다. 새로운 쿠키를 얻습니다...")
self.cookies = None
return self.make_request(url, proxy)
return response
# 사용 예
manager = CloudflareCookieManager()
response = manager.make_request(
'https://example.com/api/data',
'http://user:pass@proxy.example.com:8080'
)
IP 주소에 쿠키 바인딩
Cloudflare는 챌린지를 통과한 IP 주소에 cf_clearance를 바인딩합니다. 다른 IP에서 이 쿠키를 사용하면 차단됩니다. 회전하는 프록시를 사용할 때는 각 IP에 대해 별도의 쿠키 세트를 저장해야 합니다.
import hashlib
class IPBoundCookieManager:
def __init__(self):
self.cookies_by_ip = {}
def get_ip_hash(self, proxy_url):
"""프록시 식별을 위한 해시 생성"""
return hashlib.md5(proxy_url.encode()).hexdigest()
def get_cookies_for_proxy(self, proxy_url, target_url):
"""특정 프록시에 대한 쿠키 얻기"""
ip_hash = self.get_ip_hash(proxy_url)
if ip_hash in self.cookies_by_ip:
cookies_data = self.cookies_by_ip[ip_hash]
if cookies_data['expires'] > datetime.now():
return cookies_data['cookies']
# 브라우저를 통해 새로운 쿠키 얻기
new_cookies = self.fetch_cookies_with_browser(target_url, proxy_url)
self.cookies_by_ip[ip_hash] = {
'cookies': new_cookies,
'expires': datetime.now() + timedelta(minutes=30)
}
return new_cookies
프록시 회전 및 요청 빈도 제어하기
올바른 기술 스택이 있더라도 단일 IP에서 너무 높은 요청 빈도는 rate limiting을 유발합니다. Cloudflare는 트래픽 패턴을 분석하고 비정상적인 활동을 감지합니다.
프록시 회전 전략
회전에는 세 가지 주요 접근 방식이 있습니다: 라운드 로빈(순차적), 랜덤(무작위) 및 스티키 세션(세션에 바인딩). Cloudflare를 우회하기 위해서는 IP에 대한 요청 제한이 있는 스티키 세션 전략이 최적입니다.
import time
import random
from collections import defaultdict
from datetime import datetime, timedelta
class SmartProxyRotator:
def __init__(self, proxy_list, max_requests_per_ip=20, cooldown_minutes=10):
self.proxy_list = proxy_list
self.max_requests_per_ip = max_requests_per_ip
self.cooldown_minutes = cooldown_minutes
# 사용 카운터
self.usage_count = defaultdict(int)
self.last_used = {}
self.cooldown_until = {}
def get_proxy(self):
"""다음 사용 가능한 프록시 얻기"""
available_proxies = []
for proxy in self.proxy_list:
# 쿨다운 확인
if proxy in self.cooldown_until:
if datetime.now() < self.cooldown_until[proxy]:
continue
else:
# 쿨다운 후 카운터 초기화
self.usage_count[proxy] = 0
del self.cooldown_until[proxy]
# 요청 제한 확인
if self.usage_count[proxy] < self.max_requests_per_ip:
available_proxies.append(proxy)
if not available_proxies:
# 모든 프록시가 쿨다운 상태일 경우 대기
wait_time = min(
(self.cooldown_until[p] - datetime.now()).total_seconds()
for p in self.cooldown_until
)
print(f"모든 프록시가 쿨다운 상태입니다. {wait_time:.0f}초 대기 중...")
time.sleep(wait_time + 1)
return self.get_proxy()
# 사용이 가장 적은 프록시 선택
proxy = min(available_proxies, key=lambda p: self.usage_count[p])
self.usage_count[proxy] += 1
self.last_used[proxy] = datetime.now()
# 제한에 도달하면 쿨다운 설정
if self.usage_count[proxy] >= self.max_requests_per_ip:
self.cooldown_until[proxy] = datetime.now() + timedelta(
minutes=self.cooldown_minutes
)
print(f"프록시 {proxy}가 제한에 도달했습니다. {self.cooldown_minutes}분 쿨다운.")
return proxy
def add_delay(self):
"""요청 간 무작위 지연 추가 (사람 모방)"""
delay = random.uniform(2, 5) # 2-5초
time.sleep(delay)
# 사용 예
proxy_pool = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
'http://user:pass@proxy3.example.com:8080',
# ... 안정적인 작업을 위한 50-100개의 프록시
]
rotator = SmartProxyRotator(
proxy_pool,
max_requests_per_ip=15, # 보수적인 값
cooldown_minutes=15
)
# 요청 실행
for i in range(1000):
proxy = rotator.get_proxy()
response = requests.get(
'https://example.com/page',
proxies={'http': proxy, 'https': proxy},
headers=get_random_headers()
)
print(f"요청 {i+1}: {response.status_code}")
rotator.add_delay()
적응형 rate limiting
더 발전된 접근 방식은 서버의 응답에 따라 요청 빈도를 동적으로 조정하는 것입니다. 429 오류나 캡차가 발생하기 시작하면 자동으로 속도를 줄입니다.
class AdaptiveRateLimiter:
def __init__(self, initial_delay=3.0):
self.delay = initial_delay
self.min_delay = 1.0
self.max_delay = 30.0
self.success_streak = 0
self.failure_streak = 0
def on_success(self):
"""성공적인 요청 - 속도를 높일 수 있음"""
self.success_streak += 1
self.failure_streak = 0
if self.success_streak >= 10:
# 10% 지연 감소
self.delay = max(self.min_delay, self.delay * 0.9)
self.success_streak = 0
def on_failure(self, status_code):
"""오류 발생 - 속도를 줄임"""
self.failure_streak += 1
self.success_streak = 0
if status_code == 429: # Rate limit
# 공격적으로 지연
self.delay = min(self.max_delay, self.delay * 2.0)
elif status_code == 403: # 차단 가능성
self.delay = min(self.max_delay, self.delay * 1.5)
print(f"지연이 {self.delay:.2f}s로 증가했습니다.")
def wait(self):
"""다음 요청 전 대기"""
# ±20%의 무작위성 추가
actual_delay = self.delay * random.uniform(0.8, 1.2)
time.sleep(actual_delay)
우회를 위한 기성 도구 및 라이브러리
처음부터 자체 솔루션을 개발하는 것은 시간과 전문 지식이 필요합니다. Cloudflare 우회를 자동화하는 기성 도구가 존재합니다.
cloudscraper (Python)
cloudscraper 라이브러리는 requests 위에 구축된 라이브러리로, JavaScript 챌린지를 자동으로 해결합니다. 기본 보호와 함께 작동하지만 고급 검사를 처리하지 못할 수 있습니다.
import cloudscraper
# 프록시 지원 스크래퍼 생성
scraper = cloudscraper.create_scraper(
browser={
'browser': 'chrome',
'platform': 'windows',
'desktop': True
}
)
# 프록시 설정
proxies = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
# 요청 실행
response = scraper.get('https://example.com', proxies=proxies)
if response.status_code == 200:
print("성공적으로 우회했습니다.")
print(response.text)
else:
print(f"오류: {response.status_code}")
FlareSolverr (범용)
FlareSolverr는 Cloudflare 챌린지를 해결하기 위해 헤드리스 브라우저를 실행하는 프록시 서버입니다. HTTP API를 통해 작동하며, 모든 프로그래밍 언어를 지원합니다.
# Docker를 통해 FlareSolverr 실행
docker run -d \
--name=flaresolverr \
-p 8191:8191 \
-e LOG_LEVEL=info \
ghcr.io/flaresolverr/flaresolverr:latest
# Python에서 사용하기
import requests
def solve_cloudflare(url, proxy=None):
flaresolverr_url = "http://localhost:8191/v1"
payload = {
"cmd": "request.get",
"url": url,
"maxTimeout": 60000
}
if proxy:
payload["proxy"] = {
"url": proxy
}
response = requests.post(flaresolverr_url, json=payload)
result = response.json()
if result['status'] == 'ok':
return {
'html': result['solution']['response'],
'cookies': result['solution']['cookies'],
'user_agent': result['solution']['userAgent']
}
else:
raise Exception(f"FlareSolverr 오류: {result['message']}")
# 사용 예
result = solve_cloudflare(
'https://example.com',
proxy='http://user:pass@proxy.example.com:8080'
)
print(result['html'])
undetected-chromedriver
Selenium ChromeDriver의 패치 버전으로, 자동으로 여러 가지 anti-detect 기법을 적용합니다. Playwright보다 사용하기 쉽지만 유연성은 떨어집니다.
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def bypass_with_uc(url, proxy):
options = uc.ChromeOptions()
options.add_argument(f'--proxy-server={proxy}')
options.add_argument('--disable-blink-features=AutomationControlled')
driver = uc.Chrome(options=options, version_main=120)
try:
driver.get(url)
# Cloudflare 챌린지가 사라질 때까지 대기
WebDriverWait(driver, 20).until_not(
EC.presence_of_element_located((By.ID, "cf-spinner-please-wait"))
)
# 신뢰성을 위해 추가 대기
time.sleep(3)
# 결과 얻기
html = driver.page_source
cookies = driver.get_cookies()
return {'html': html, 'cookies': cookies}
finally:
driver.quit()
# 사용 예
result = bypass_with_uc(
'https://example.com',
'http://user:pass@proxy.example.com:8080'
)
복합 접근법: 최적의 전략은 헤드리스 브라우저를 쿠키를 최초로 얻기 위해서만 사용하고, 이후에는 이 쿠키를 사용하여 HTTP 클라이언트(tls-client, cloudscraper)로 전환하는 것입니다. 이는 신뢰성과 성능 간의 균형을 제공합니다.
결론
프록시를 통한 Cloudflare 우회는 올바른 TLS 지문, 진짜 HTTP 헤더, 품질 좋은 프록시 및 세션 관리가 필요합니다. 주요 권장 사항은 다음과 같습니다:
- 주거용 또는 모바일 프록시를 데이터 센터 대신 사용하세요.
- 올바른 TLS 지문을 가진 라이브러리(tls-client, curl-impersonate)를 사용하세요.
- 복잡한 경우에는 anti-detect 패치가 있는 헤드리스 브라우저를 사용하세요.
- cf_clearance 쿠키를 저장하고 재사용하세요.
- IP당 15-20개의 요청 제한을 고려하여 프록시를 회전하세요.
- 요청 간 무작위 지연(2-5초)을 추가하세요.
Cloudflare의 보호는 지속적으로 진화하고 있으므로 도구를 정기적으로 업데이트하고 전략을 조정하는 것이 중요합니다. 지문 기술의 변화를 모니터링하고 최신 보호 버전에서 솔루션을 테스트하세요.
안정적인 작업을 위해서는 폭넓은 IP 풀과 자동 회전을 제공하는 전문 프록시 서비스를 사용하는 것이 좋습니다.