내 스크래퍼를 생존하게 만드는 80줄의 Python 코드: 재시도, 상태 체크 및 페일오버(Failover)를 포함한 프록시 로테이션
요약
단순한 프록시 로테이션을 넘어 차단, 타임아웃, 캡차 대응을 포함한 견고한 Python 기반 프록시 로테이터 구현 방법을 소개합니다. 상태 추적과 지수 백오프를 통해 스크래퍼의 생존력을 높이는 실전 코드를 제공합니다.
핵심 포인트
- 단순 랜덤 선택이 아닌 상태 추적 기반의 프록시 관리 필요
- 실패 유형(타임아웃, 429, 캡차 등)에 따른 차별화된 대응 전략
- 지수 백오프를 활용한 실패 프록시의 일시적 제외 및 복귀 메커니즘
- 스레드 안전을 보장하는 라운드 로빈 방식의 로테이션 구현
프록시를 로테이션하는 것은 쉽습니다. 하지만 차단(ban), 타임아웃(timeout), 그리고 죽은 프록시로부터 살아남는 것은 쉽지 않습니다. 상태 추적과 페일오버(Failover) 기능이 포함된 80줄의 Python 로테이터 — 바로 복사해서 사용할 수 있습니다.
요약 (TL;DR): 프록시 리스트를 로테이션하는 것은 쉽습니다. 하지만 프록시가 차단되거나, 타임아웃이 발생하거나, 쓰레기 데이터를 반환할 때 스크래퍼가 죽지 않도록 로테이션하는 것이 사람들이 실수하는 부분입니다. 아래는 재시도(retries), 상태 추적(health tracking), 그리고 페일오버(failover)를 수행하는 가볍고 의존성이 적은 소규모 프록시 로테이터입니다 — 바로 복사해서 사용할 수 있습니다.
대규모로 스크래핑을 하고 있다면 이미 그 고통을 알고 계실 것입니다. 프록시 리스트를 루프(loop)에 넣고 돌리면, 10분 동안은 잘 작동하다가 절반은 속도 제한(rate-limited)에 걸리고, 3개는 죽어 있으며, 하나는 HTTP 200 응답과 함께 캡차(captcha) 페이지를 반환하고, 당신의 작업은 소리 없이 쓰레기 데이터를 만들어냅니다. 단순한 random.choice(proxies) 방식으로는 충분하지 않습니다.
이 포스트는 프록시를 있는 그대로 — 즉, 신뢰할 수 없는 리소스 풀(resource pool) — 로 취급하며, 그럼에도 불구하고 스크래퍼를 계속 실행 상태로 유지하는 로테이터를 구축합니다.
실제 로테이터에 필요한 것
코드를 작성하기 전에, 단순한 장난감 수준과 실제 운영 환경(production)에서 실행할 수 있는 수준을 가르는 요구사항은 다음과 같습니다:
- 로테이션 (Rotation) — 단일 IP에 요청이 집중되지 않도록 풀(pool) 전체에 요청을 분산합니다.
- 재시도 (Retries) — 실패한 요청은 동일한 죽은 프록시가 아니라 다른 프록시로 재시도해야 합니다.
- 상태 추적 (Health tracking) — 반복적으로 실패하는 프록시는 제외(bench)시키고, 회복된 프록시는 다시 복귀시킵니다.
- 실패 분류 (Failure classification) — 타임아웃, 407, 429, 그리고 캡차 페이지는 서로 다른 문제이며 동일하게 취급해서는 안 됩니다.
- 가벼운 의존성 (No heavy dependencies) — 오직
requests만 사용합니다. (마지막에 비동기(async) 버전이 포함됩니다.)
1단계: 상태(health)를 가진 프록시 모델링
각 프록시는 단순한 문자열 그 이상입니다. 성공하거나 실패함에 따라 조정되는 점수(score)를 가집니다.
import time
from dataclasses import dataclass, field
...
지수 백오프(exponential backoff)에 주목하세요. 계속해서 실패하는 프록시는 완전히 망가질 때까지 재시도하는 대신, 매번 더 긴 시간 동안 제외됩니다.
2단계: 로테이터
import itertools
import threading
...
itertools.cycle은 라운드 로빈 (round-robin) 방식의 회전을 제공하며, lock을 통해 스레드 안전 (thread-safe)을 보장하므로 스레드 풀 (thread pool)에서 사용할 수 있습니다.
3단계: 재시도 및 분류 기능이 포함된 요청 래퍼 (request wrapper)
이 부분이 실제로 여러분의 업무를 구해줄 핵심입니다. 우리는 무엇이 잘못되었는지 분류하고 그에 따라 적절히 대응합니다.
import requests
# 이것들을 "프록시 또는 대상이 우리를 거부함"으로 취급합니다
...
대부분의 튜토리얼이 놓치는 핵심 세부 사항:
- 캡차 (captcha)가 포함된 200 응답은 실패입니다. 상태 코드 (status code)만 확인하는 것으로는 충분하지 않습니다. 사이트들은 차단 페이지에서 끊임없이 200을 반환합니다.
- 407은 프록시 인증 (proxy auth) 실패를 의미합니다. 대상 사이트를 탓하지 말고 프록시에 페널티를 부여하세요.
- 우리는 매 시도마다 새로운 프록시로 재시도합니다. 이미 죽은 동일한 프록시로 재시도하는 것은 무의미하기 때문입니다.
4단계: 사용하기
PROXIES = [
"http://user:pass@p1.provider.com:8000",
"http://user:pass@p2.provider.com:8000",
...
코드를 실행하면서 프록시들이 제외되었다가 다시 돌아옴에 따라 alive 수치가 줄어들고 회복되는 것을 관찰해 보세요. 이러한 가시성(visibility) 하나만으로도 어떤 제공업체의 마케팅 페이지보다 여러분의 프록시 풀 (proxy pool)의 실제 품질에 대해 더 많은 것을 배울 수 있을 것입니다.
비동기 버전 (실질적인 처리량을 위해)
requests는 동기식 (synchronous)입니다. 수천 개의 URL을 처리하려면, 동일한 로테이터 (rotator)와 비동기 페치 (async fetch)를 사용하는 aiohttp 핵심 코드가 필요합니다:
import aiohttp
import asyncio
...
(참고: aiohttp 프록시 인증은 proxy URL에 자격 증명이 포함되어야 하며, 우리의 형식은 이미 이를 갖추고 있습니다.)
이 코드가 해결할 수 없는 것들
솔직한 한계점을 말씀드리자면, 여러분의 로테이터는 이를 공급하는 프록시의 품질만큼만 성능을 발휘합니다.
- 로테이터는 나쁜 풀 (pool)을 고칠 수 없습니다. 만약 제공업체가 이미 플래그(flagged)된 데이터센터 IP를 준다면, 어떤 재시도 로직도 여러분을 구할 수 없습니다. 죽은 무게를 회전시키는 것은 여전히 죽은 무게일 뿐입니다.
- 너무 공격적인 회전은 상태 유지 작업 (stateful tasks, 로그인, 장바구니 등)에서 차단을 유발할 수 있습니다. 계정 관련 작업에는 회전이 아니라 스티키 (sticky) 세션이 필요합니다. 회전은 상태가 없는 (stateless) 스크래핑을 위한 것입니다.
- 무료 프록시 리스트를 사용하면 풀의 90%가 죽어 있기 때문에 이 코드가 고장 난 것처럼 보일 것입니다. 로직은 정상입니다. 입력값이 쓰레기일 뿐입니다.
풀(Pool) 자체를 위해서는 실제 세션 제어(session control)가 가능한 주거용(residential) 또는 모바일(mobile) IP가 필요합니다. 저는 ProxyUniverse를 통해 이를 운영합니다. 이곳의 주거용, 모바일 및 ISP 프록시는 표준 host:port:user:pass 형식으로 제공되므로, 별도의 수정 없이 위에서 언급한 PROXIES 리스트에 바로 적용할 수 있습니다. 2026년 공급업체 폐쇄 사태 이후, 저는 하나의 공급업체가 중단되더라도 로테이터(rotator)가 완전히 비지 않도록 풀을 여러 네트워크에 분산하여 유지하고 있습니다.
마무리 (Wrap-up)
10분 동안 작동하는 스크래퍼와 10일 동안 작동하는 스크래퍼의 차이는 파싱(parsing) 코드 때문인 경우가 거의 없습니다. 핵심은 프록시 계층(proxy layer)의 실패를 어떻게 처리하느냐에 있습니다. 상태 추적(Health tracking) + 분류된 재시도(classified retries) + 페일오버(failover)는 아마 60줄 정도의 코드일 것이지만, 바로 그 60줄이 가장 중요합니다.
코드를 가져가서 여러분의 프록시를 넣고 alive 카운터를 지켜보세요. 만약 이 코드를 기반으로 무언가(프록시 점수화, 도메인별 풀, Prometheus 메트릭 등)를 구축하신다면, 진심으로 보고 싶습니다.
댓글을 위한 질문: 여러분은 소프트 블록(soft blocks) — 즉, 깨끗한 HTTP 200 응답과 함께 돌아오는 캡차(captcha) 페이지나 쓰레기 HTML(junk HTML) — 을 어떻게 감지하시나요? 이것이 이 로테이터가 가장 취약하게 다루는 실패 유형이며, 사람들이 실제 운영 환경(production)에서 어떤 체크를 수행하는지 궁금합니다. 👇
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기