[문제 해결] 웹 크롤링 CloudFlare Turnstile 우회하기

2025. 7. 8. 23:30문제 해결

이번 포스팅에선 웹사이트를 크롤링할 때 마주할 수 있는 cloudflare의 turnstile을 우회하는 방법에 대해서 알아보겠습니다.

 

 

cloudflare turnstile이란?

cloudflare turnstile은 CAPTCHA를 표시하지 않고도 간단히 작동하는 봇 탐지 시스템입니다.

turnstile은 일련의 소규모 비대화형 JavaScript 챌린지를 실행하여 사용자가 봇인지 실제 유저인지 판단하는 점수를 매깁니다.

챌린지에는 작업 증명, 공간 증명, 웹 API 탐색, 그리고 브라우저 특이 사항 및 사용자 행동을 감지하는 다양한 챌린지가 포함됩니다.

 

https://developers.cloudflare.com/turnstile/

 

Cloudflare Turnstile

Turnstile can be embedded into any website without sending traffic through Cloudflare and works without showing visitors a CAPTCHA.

developers.cloudflare.com

 

turnstile 위젯은 다음과 같은 유형이 있습니다.

  • 상호작용이 없는 챌린지. (유저일 가능성이 높다면 체크박스를 진행하지 않고 그냥 넘어갑니다)
  • 방문자가 봇으로 의심되는 경우, 방해가 되지 않는 상호작용형 챌린지. 해당 경우 체크박스를 실행합니다.

크롤링을 할 경우 cloudflare turnstile에서 점수를 낮게 받아 봇으로 의심되기 쉽습니다.

따라서 체크박스 클릭을 진행해야하는 동작이 추가되어 크롤링이 더이상 진행하지 못하게 되는 상황이 발생할 수 있습니다.

 

cloudflare turnstile을 우회하기 위한 방법으로 크게 3가지를 생각해 볼 수 있습니다.

 

  1. 실제 gui 조작으로 화면의 체크박스를 클릭하기
  2. cloudflare turnstile을 통과한 유저에게 부여되는 쿠키를 이용하기
  3. python + cloudflare 우회용 라이브러리 이용하기

 

실제 gui 조작으로 화면의 체크박스를 클릭하기

우선 cloudflare turnstile이 진행되는 페이지의 html 코드를 살펴보며 어디에 체크박스 태그가 존재하는지 살펴보아야합니다.

 

 

위 사진에서 직접 찾아서 눌러야할 체크박스 input 태그가 shadow DOM에 속해있는 모습을 볼 수 있습니다. 

 

shadow root의 mode가 open mode였다면 웹 드라이버가 해당 DOM tree에 접근할 수 있었겠지만 위와 같이 closed mode인 경우 정상적인 접근은 불가합니다. 

mode 종류 접근 여부 접근 방법
shadowRoot mode: "open" 개발자 도구나 자바스크립트로 접근 가능 ✅ 가능 (element.shadowRoot)
shadowRoot mode: "closed" 외부에서 .shadowRoot로 접근 자체가 불가 불가능 (element.shadowRoot는 null 반환됨)

 

또한 크롤링 해야하는 페이지에서 요소의 위치가 업데이트가 이루어진다면

그에 맞게 체크박스를 찾아서 업데이트 해야하는 과정이 계속 반복됨으로 원초적인 접근방법이지만 장기적으로 보았을 때는 좋은 선택지가 아닙니다.

 

 

cloudflare turnstile을 통과한 유저에게 부여되는 쿠키를 이용하기

cloudflare 검증에서 통과한 유저에게 발급되는 쿠키가 있습니다.

 

https://developers.cloudflare.com/fundamentals/reference/policies-compliances/cloudflare-cookies/

 

Cloudflare Cookies

Cloudflare uses various cookies to maximize network resources, manage traffic, and protect our customers’ sites from malicious traffic.

developers.cloudflare.com

 

필자가 크롤링 할 페이지의 경우 사용자에게 __cf_bm, cf_clearance 라는 이름의 쿠키가 발급되고 있음을 확인할 수 있었습니다.

 

 

turnstile 검증을 직접 통과해 해당 쿠키 값을 획득한 후

하드코딩하여 크롤링 요청 시 쿠키를 같이 보내주는 로직을 생각해 볼 수 있습니다.

(개발자 도구 -> Application -> Cookies)

(해당 쿠키를 수동으로 획득한 User-Agent와 크롤링 요청 시 설정한 User-Agnet가 같아야합니다.)

 

다음은 수동으로 획득한 쿠키를 주입하여 크롤링하는 로직입니다. 

(Java로 작성되어있습니다.)

try {
    // 1. 반드시 해당 도메인에 먼저 접속해서 쿠키를 심을 수 있는 상태로 만든다
    driver.get("https://www.example.com"); // Cloudflare가 걸려있는 사이트의 홈 주소

    Thread.sleep(2000); // 페이지 로딩 대기

    // 2. 발급받은 쿠키를 주입 (Turnstile 우회에 사용됨)
    Cookie cfClearance = new Cookie.Builder("cf_clearance",
            "your_cf_clearance_token_here")
            .domain(".example.com")
            .path("/")
            .isHttpOnly(true)
            .isSecure(true)
            .build();
            
    Cookie cfBm = new Cookie.Builder("__cf_bm",
            "your_cf_bm_token_here")
            .domain(".example.com")
            .path("/")
            .isHttpOnly(true)
            .isSecure(true)
            .build();

    driver.manage().addCookie(cfClearance);
    driver.manage().addCookie(cfBm);

    // 3. 본격적인 크롤링 대상 URL로 이동
    driver.navigate().to("https://www.example.com/protected-page");

    // 봇 탐지 우회를 위한 랜덤 딜레이
    Thread.sleep(2000 + (int)(Math.random() * 2000));

} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

 

하지만 이 방식은 첫번째 시도에는 성공할 확률이 높으나 그 다음부터는 실패할 확률이 매우 올라갑니다.

 

우선 쿠키는 세션 및 브라우저 환경과 연결되어서 검증됩니다.

Cloudflare는 쿠키뿐 아니라 다음 정보를 함께 검증합니다.

 

항목 설명
User-Agent 쿠키 발급 당시 브라우저와 일치해야 함
IP 주소 동일한 네트워크/프록시에서만 유효
브라우저 지문 (Fingerprint) 쿠키 발급 시 브라우저의 해상도, 플랫폼, 언어 등 정보 저장
헤더 순서 및 구조 쿠키 발급 당시 요청 헤더 순서까지 포함해서 기억함

 

즉, 쿠키만 같아도 환경이 다르면 유효하지 않게 처리됩니다.

 

또한 다음과 같은 이유로 자동화 웹드라이버임을 감지하고 점수가 깎이게 됩니다.

감지 항목 설명
navigator.webdriver=true 자동화 브라우저 표시됨 (비활성화 필요)
WebGL, AudioContext, Canvas 렌더링 정보가 일반 브라우저와 다름
헤더 순서 이상 일반 브라우저가 보내는 헤더 순서와 다름
키보드/마우스 이벤트 없음 사람처럼 동작하지 않으면 의심

 

또한 쿠키의 유효시간이 짧은 경우 쿠키를 이용하는 방법은 비효율적입니다.

 

 

python + cloudflare 우회용 라이브러리 이용하기

파이썬에 cloudflare를 우회하기 위한 라이브러리가 많이 존재합니다.

그 중 selelniumBase + uc mode(undetected chromedriver mode) 조합으로 cloudflare 우회가 가능합니다.

 

seleniumBase란?

seleniumbase는 웹 자동화 활동을 위한 전문 툴킷입니다. 웹사이트 테스트, CAPTCHA 우회, 생산성 향상, 작업 완료, 비즈니스 확장을 위해 설계되었습니다.

driver 직접 생성 없이 with SB() as sb:로 바로 크롬 실행이 가능합니다.

브라우저 설정 (uc=True, locale="ko", test=True) 등도 간결하게 처리 가능합니다.

셀레니움보다 직관적인 함수명이 많아서 복잡한 코드 없이 테스트 흐름을 구성할 수 있습니다.

 

https://seleniumbase.io/

 

SeleniumBase Docs

SeleniumBase All-in-one Browser Automation Framework:Web Crawling / Testing / Scraping / Stealth 🚀 Start | 🏰 Features | 🎛️ Options | 📚 Examples | 🌠 Scripts | 📱 Mobile 📘 APIs | 🔠 Formats | 🔴 Recorder | 📊 Dashboard | 🗾 Loca

seleniumbase.io

 

그 중 SB(SeleniumBase Context Manager)는 SeleniumBase의 실행 환경을 열고 브라우저를 제어할 수 있게 해줍니다.

 

여기서 uc=True 인자는 Undetected Chrome 설정을 켜는 것입니다. 일반적인 크롤링 탐지를 우회하는 데 사용됩니다.

추가적으로 locale 인자는 페이지 언어 설정을 한국어로 맞춰주는 역할, test 인자는 테스트용 로그와 스크린샷 저장 등의 기능을 활성화 할 수 있습니다.

 

undetected chromedriver란?

Selenium에서 웹 사이트 자동화 감지를 우회하기 위해 사용되는 Python 라이브러리입니다. 

기존 ChromeDriver와는 달리, 웹 사이트가 스크래퍼를 감지하는 방법을 속여 자동화된 브라우저 사용을 숨길 수 있도록 해줍니다. 

 

 

우선 seleniumBase를 쓰기 위해서 패키지를 설치해줍니다.

pip install seleniumbase

 

 

다음과 같은 예시코드를 실행시켜 어떻게 turnstile을 우회하는지 살펴볼 수 있습니다.

from seleniumbase import SB

with SB(uc=True, test=True) as sb:
    url = "https://seleniumbase.io/apps/turnstile"
    sb.uc_open_with_reconnect(url)
    sb.uc_gui_click_captcha()
    sb.assert_element("img#captcha-success", timeout=3)
    sb.set_messenger_theme(location="top_left")
    sb.post_message("SeleniumBase wasn't detected", duration=3)

위 코드는 체크박스가 나타나면 직접 클릭해서 검증을 통과하는 것을 자동화해놓은 코드입니다.

직접 체크박스를 클릭하는 것 이외에도 uc mode를 이용해 보다 안전한 접속 시도를 통해서 우회할 수 있는 코드도 있습니다.

 

 

다음은 seleniumbase를 이용해서 크롤링을 하는 코드입니다.

해당 방법으로 크롤링할 시 cloudflare turnstile 검증을 우회해서 크롤링할 수 있었습니다. (2025.07 기준)

from selenium.webdriver.common.by import By
from seleniumbase import SB
import time

# 샘플 대상 URL (Cloudflare Turnstile이 활성화된 사이트)
TARGET_SITE = "https://www.example.com"
TARGET_PATHS = {
    "페이지1": "https://www.example.com/page1",
    "페이지2": "https://www.example.com/page2",
    ...
}

with SB(uc=True, headless2=True, test=True) as sb:
    # 1. 도메인 진입 → 쿠키 발급 유도
    sb.uc_open_with_reconnect(TARGET_SITE, reconnect_time=10)

    # Turnstile challenge 우회 확인
    if not sb.driver.get_cookie("cf_clearance"):
        raise RuntimeError("cf_clearance 쿠키 발급 실패 → Headless 차단 중")

    for page, url in TARGET_PATHS.items():

        sb.uc_open_with_reconnect(url, reconnect_time=10)

        # Cloudflare 이미지 요소 등 확인 (정상 로딩 체크)
        sb.assert_element("img[src^='https://image.example.com']", timeout=3)

        if not sb.driver.get_cookie("cf_clearance"):
            raise RuntimeError("cf_clearance 쿠키 소실 → Turnstile 재발생")

        # 대상 요소 수집
        cards = sb.driver.find_elements(By.CSS_SELECTOR, ".item_card")

        # 크롤링 로직

		...

 

sb.uc_open_with_reconnect()

파라미터 설명
url 접속할 대상 URL
reconnect_time 재시도 총 대기 시간 (초). 실패 시 이 시간 안에 여러 번 재시도

 

이 메서드는 Cloudflare의 Bot 차단 우회를 목적으로 한 안전한 접속 시도입니다. 내부적으로 다음과 같은 과정을 수행합니다.

  1. driver.get(url)로 페이지 열기 시도
  2. 페이지가 Cloudflare Challenge / Turnstile / Spinner 등에 막혀 cf_clearance 쿠키가 설정되지 않으면,
  3. Headless 우회 / User-Agent 변경 / reconnect 반복 등의 방법으로 지정된 reconnect_time 내에서 계속 재접속을 시도
  4. 그 시간 안에 정상 응답(Cookie 발급 포함)이 이루어지면 성공
  5. 실패하면 RuntimeError 또는 Timeout이 발생

이 메서드는 uc=True 옵션과 함께 사용할 때만 의미가 있습니다. (undetected-chromedriver 기반이기 때문)

또한 reconnect_time은 너무 짧게 주면 우회 실패 가능성이 커지고, 너무 길게 주면 속도가 저하될 가능성이 있습니다.

 

 

결과적으로 seleniumbase를 활용해 위의 2가지 방법보다 손쉽게 cloudflare를 우회하여 크롤링할 수 있었습니다.

 

 

 

 

 

+ seleniumbase 추가자료

https://github.com/seleniumbase/SeleniumBase/discussions/2322

 

Using SeleniumBase in UC mode · seleniumbase SeleniumBase · Discussion #2322

Hi, Does SeleniumBase automatically set the user agent in UC mode? If so, is it randomized for each session, or does it remain the same across different sessions? Is there any risk of detection if ...

github.com

 

 

'문제 해결' 카테고리의 다른 글

격변하는 요즘 시대의 AI에 관하여  (0) 2026.03.13
[문제 해결] CORS 에러 해결하기  (0) 2025.11.26