Python에서 대규모로 스크래핑하거나 크롤링하거나 무엇이든 자동화하다 보면, 머지않아 대상 사이트는 모든 요청이 하나의 IP에서 온다는 것을 알아채고 차단합니다. Python에서 프록시 로테이션은 트래픽을 여러 IP 주소로 분산시켜, 어느 한 IP도 rate limit에 걸리거나 차단당하지 않도록 합니다. 이 가이드는 requests, httpx, aiohttp를 위한 정확한 코드와, 로테이팅 게이트웨이와 관리형 IP 풀 중 무엇을 선택할지를 보여줍니다.
아래의 모든 것을 좌우하는 두 가지 핵심 결정이 있습니다. 첫째: rotating vs sticky 세션 — 요청마다 새 IP를 쓸지, 아니면 여러 단계의 흐름 동안 하나의 IP를 유지할지. 둘째: residential vs datacenter IP — 프록시 유형 결정 매트릭스를 참고하세요. 이 두 가지를 제대로 잡으면 코드는 쉬운 부분입니다.
1. 게이트웨이 로테이션 (권장). 하나의 엔드포인트에 연결하면 새 연결마다 제공자가 신선한 exit IP를 건네줍니다. 관리할 목록도, 솎아낼 죽은 IP도 없습니다. JIBAO의 dynamic residential 풀이 이 방식으로 동작합니다: 동일한 host와 port, 요청마다 새 IP.
2. 직접 관리하는 IP 목록. host:port 항목 목록을 보유하고 요청마다 하나씩 고릅니다. 제어권은 더 크지만, health-checking, 재시도, ban 추적을 직접 책임져야 합니다. 알려진 안정적인 주소가 필요할 때 전용 datacenter IP와 함께 쓰기에 유용합니다.
가장 단순한 경우 — 당신을 위해 로테이션해 주는 게이트웨이로 요청 하나를 라우팅합니다. socks5h://를 사용하세요(끝의 h는 DNS가 프록시 쪽에서 해석된다는 뜻으로, 조회 정보가 새어 나가는 것을 막아 줍니다):
import requests
# JIBAO rotating residential gateway: a new exit IP per connection
PROXY = "socks5h://USERNAME:[email protected]:10001"
proxies = {"http": PROXY, "https": PROXY}
for _ in range(5):
r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=30)
print(r.json()["origin"]) # a different IP each loop
requests가 SOCKS5를 쓰게 하려면 추가 패키지를 설치하세요: pip install "requests[socks]". HTTP 프록시 엔드포인트를 선호한다면 스킴을 http://로 바꾸기만 하면 됩니다 — 나머지는 동일합니다.
import random
import requests
PROXY_POOL = [
"socks5h://USERNAME-country-us:[email protected]:10001",
"socks5h://USERNAME-country-uk:[email protected]:10001",
"socks5h://USERNAME-country-de:[email protected]:10001",
]
def fetch(url):
proxy = random.choice(PROXY_POOL)
return requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
resp = fetch("https://example.com")
print(resp.status_code)
로그인 흐름, 장바구니, 페이지네이션 결과는 작업 도중 IP가 바뀌면 깨집니다. username에 세션 토큰을 추가해 IP를 고정하세요. 같은 토큰을 재사용하면 그 수명 동안 동일한 exit IP를 유지합니다:
import requests
SESSION_ID = "task-0001"
PROXY = f"socks5h://USERNAME-session-{SESSION_ID}:[email protected]:10001"
s = requests.Session()
s.proxies = {"http": PROXY, "https": PROXY}
s.post("https://example.com/login", data={"u": "me", "p": "secret"})
s.get("https://example.com/dashboard") # same IP, session intact
httpx는 HTTP/2와 네이티브 async를 갖춘 현대적인 클라이언트를 제공합니다. 최신 버전은 단일 proxy= 인자를 받는다는 점에 유의하세요:
import httpx
PROXY = "socks5h://USERNAME:[email protected]:10001"
with httpx.Client(proxy=PROXY, timeout=30) as client:
for _ in range(5):
print(client.get("https://httpbin.org/ip").json()["origin"])
httpx의 SOCKS 지원에는 pip install "httpx[socks]"가 필요합니다. HTTP/2를 쓰려면 클라이언트에 http2=True를 추가하세요 — 트래픽이 실제 브라우저처럼 보이게 만들어 주며, TLS 지문 매칭과 잘 어울립니다.
높은 처리량을 위해 많은 요청을 한꺼번에 발사하세요. 게이트웨이를 통한 각 연결은 자체 exit IP를 갖기 때문에, 동시성과 로테이션이 함께 이루어집니다:
import asyncio
import aiohttp
from aiohttp_socks import ProxyConnector
PROXY = "socks5://USERNAME:[email protected]:10001"
async def fetch(url):
connector = ProxyConnector.from_url(PROXY)
async with aiohttp.ClientSession(connector=connector) as session:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=30)) as r:
return await r.text()
async def main():
urls = ["https://httpbin.org/ip"] * 20
results = await asyncio.gather(*(fetch(u) for u in urls))
print(f"fetched {len(results)} pages")
asyncio.run(main())
SOCKS connector는 pip install aiohttp_socks로 설치하세요. 작업마다 새 connector를 만들어 각 요청이 새로운 프록시 연결을 열도록 하세요.
로테이션은 실패를 새 IP에서 재시도해야 비로소 빛을 봅니다. 지수적으로 backoff하고 시도마다 세션 토큰을 로테이션하세요:
import requests
from time import sleep
def fetch_with_retry(url, max_retries=4):
for attempt in range(max_retries):
proxy = f"socks5h://USERNAME-session-r{attempt}:[email protected]:10001"
try:
r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
if r.status_code in (403, 429):
raise requests.HTTPError(f"blocked: {r.status_code}")
r.raise_for_status()
return r.text
except requests.RequestException:
sleep(2 ** attempt) # 1s, 2s, 4s, 8s
raise RuntimeError(f"failed after {max_retries} tries: {url}")
대규모 실행 전에 IP가 실제로 바뀌는지 확인하세요. echo 엔드포인트를 몇 번 호출해 IP가 서로 다른지 점검합니다. TLS 지문이 정체를 드러내지 않는지도 확인하려면, JIBAO의 무료 JA3/TLS 지문 체커로 요청을 실행해 보세요 — 로테이팅 IP라도 한눈에 들통나는 Python 지문이라면 여전히 차단됩니다.
로테이션이 탄탄해지면, 다음으로 부딪히는 벽은 보통 IP가 아니라 지문입니다. 깨끗한 residential IP인데도 여전히 403이 뜬다면 curl_cffi로 TLS 지문을 우회하는 법과 전체 Cloudflare 우회 레시피를 읽어 보세요.
신규 사용자는 가입 시 500MB를 받고, 첫 충전 시 추가 보너스를 받습니다. 기간 한정 혜택입니다.