Python

curl_cffi 도입기: 웹 스크래핑 문제 해결하기

gudaeng 2025. 5. 11. 16:07

웹 검색 AI 에이전트 개발하면서 검색 데이터 수집할 때 자주 마주치는 문제가 있다. 사이트들이 점점 더 스크래핑을 막는 기술을 도입하면서 requests, aiohttp 같은 기본 HTTP 클라이언트로는 자꾸 403, 429 에러가 뜨는 상황.

문제 상황

기존에 써오던 HTTP 클라이언트들로는 이런 문제가 생겼다:

  • 403 Forbidden, 429 Too Many Requests 같은 오류 계속 발생
  • Cloudflare 같은 보안 솔루션이 적용된 사이트에서 CAPTCHA가 뜸
  • User-Agent 설정해도 스크래핑 방지 우회 안 됨
  • 자바스크립트로 동적 생성되는 콘텐츠는 아예 접근 불가

이런 문제는 AI 에이전트가 정확한 정보를 제공하는 능력에 직접적인 영향을 줬다.

브라우저 지문(Browser Fingerprinting) 문제

결국 근본적인 문제는 '브라우저 지문'이었다. 웹사이트가 실제 브라우저와 봇을 구분하는 방식인데, 아래 요소로 구성된다:

  1. TLS/JA3 지문: 암호화 방식, 보안 기능 등의 연결 패턴
  2. HTTP/2 통신 지문: 데이터 전송 창 크기, 우선순위 처리 방식 등
  3. HTTP 헤더 순서와 형식: 브라우저마다 다른 헤더 전송 순서
  4. 보안 연결 시작 메시지 특성

결국 User-Agent만 설정해서는 이런 복잡한 지문 요소를 모방할 수 없었다. 예를 들어 크롬은 Accept, Accept-Encoding, Accept-Language 순으로 헤더를 보내는데, 자동화 도구는 이런 세세한 패턴까지 맞추기 어렵다. Cloudflare 같은 서비스는 이런 미세한 차이로 봇을 구분한다.

curl_cffi 도입

이 문제 해결하려고 curl_cffi를 도입했다. 주요 특징은 다음과 같다:

  1. 브라우저 지문 완벽 모방
    • 크롬, 파이어폭스 등 주요 브라우저의 지문 특성 정확히 재현
    • 다양한 브라우저 버전별 지문 지원 (크롬 99부터 최신 버전까지)
  2. 빠른 성능
    • requestshttpx보다 빠름
    • 메모리 사용량도 적음
    • aiohttppycurl과 비슷한 수준의 성능
  3. 쉬운 사용법
    • requests와 유사한 인터페이스
    • 비동기 방식 지원
    • 로그인 세션 유지, 쿠키 관리 기능 제공

작동 원리

curl_cfficurl-impersonate라는 기술 기반으로 브라우저 지문을 모방한다:

  • 실제 브라우저와 똑같은 방식으로 TLS 핸드셰이크 수행
  • HTTP/2 설정값을 브라우저와 동일하게 구성
  • 헤더 순서와 형식을 정확히 유지

간단한 사용 예시:

import curl_cffi

# 크롬 브라우저 모방
response = curl_cffi.get(
    "https://example.com", 
    impersonate="chrome"
)

# 특정 버전 브라우저 지정
response = curl_cffi.get(
    "https://example.com", 
    impersonate="chrome124"
)

# 세션 유지 및 쿠키 관리
session = curl_cffi.Session()
session.get("https://example.com/login")
session.post(
    "https://example.com/login", 
    data={"username": "user", "password": "pass"}
)

비동기 요청도 쉽게 처리할 수 있다:

import asyncio
from curl_cffi import AsyncSession

async def fetch_data():
    async with AsyncSession() as session:
        response = await session.get(
            "https://example.com", 
            impersonate="chrome"
        )
        return response.text

result = asyncio.run(fetch_data())

적용 결과

실제로 적용해보니 효과가 좋았다:

  1. 차단 문제 대폭 감소
    • Cloudflare 적용된 사이트도 정상적으로 접근 가능
    • 403, 429 에러 확 줄어듦
    • 이전에 접근 불가능했던 사이트에서도 안정적으로 데이터 수집 가능
  2. 성능 개선
    • Selenium 대비 메모리 사용량 8~90% 감소
    • 데이터 처리 속도 약 3배 향상
    • 실패 후 재시도 횟수 감소로 시스템 효율성 증가

메모리 사용량 벤치마크:

라이브러리 피크 메모리(MB) 메모리 증가(MB)
curl_cffi 110.55 MB 38.00 MB
requests 116.39 MB 54.75 MB
selenium 812.86 MB 723.14 MB

위 표를 보면 curl_cffi가 Selenium보다 피크 메모리 사용량이 7배 이상 적다는 걸 알 수 있다. 이게 실제 서버 운영 시에는 엄청난 차이다.

결론

curl_cffi 도입하고 나서는 스크래핑이 안정적으로 돌아갔다. 메모리 사용량도 Selenium보다 7배 적어서 서버 부하가 확 줄었다. 무엇보다 코드 수정이 거의 필요 없다는 게 큰 장점이다. HTTP/2 지원이랑 TLS/JA3 지문 모방 덕분에 예전에는 불가능했던 사이트들도 이제 대부분 문제없이 크롤링 가능하다.

비동기 지원 덕분에 여러 요청 동시에 처리할 수 있어서 속도도 빨라졌다. 기존에 보통 10개 사이트 크롤링하는데 15초 걸렸던 작업이 5초 정도로 줄었다.

기존 라이브러리들이랑 비교해 봤을 때, curl_cffi는 fingerprinting 우회 기능이 강점이면서도 속도도 전혀 뒤쳐지지 않는다. 게다가 최근 v0.11.0 베타부터는 HTTP/3까지 지원되니 앞으로 계속 쓸 가치가 있는 라이브러리다.

물론 웹사이트 이용약관 체크하고 서버에 과부하 주지 않도록 조심하는 건 기본이다. 스크래핑도 서로 공존하는 방식으로 해야 오래 쓸 수 있으니까.

'Python' 카테고리의 다른 글

Python - 이벤트 루프  (0) 2025.04.20
Python thread  (0) 2025.04.20