KYC 없이 — 신분증, 셀카, 문서 없이. 암호화폐로 결제: BTC, USDT, USDC, ETH, SOL, LTC
언어: 한국어
시작하기

문서 · Python 3.9+ · requests · httpx · aiohttp

Python에서 회전하는 레지덴셜 프록시: requests, httpx, aiohttp

한줄 코드가 아닌 프로덕션 패턴: Retry-After를 준수하는 재시도, 요청마다 새 IP 또는 플로우당 IP, asyncio 내 호스트별 동시성, 실제 페이지당 바이트 사용량.

마지막 업데이트: · 6 분 읽기

빠른 답변

Python에서 회전형 레지덴셜 프록시는 어떻게 사용하나요?

requests에서는 http://USERNAME:[email protected]:7777를 http와 https 모두의 프록시로, httpx 0.26+에서는 proxy=로, aiohttp에서는 요청별 proxy=로 전달하세요. CrawlRelay 레지덴셜 프록시는 새 연결마다 회전하며 사용자명에 -session-<id> 포함 시 고정됩니다. 1 GB는 US$1.50, 50 GB 이상은 US$0.50/GB 비용입니다. 암호화폐 결제, KYC 없이 이용 가능합니다.

Python 설정 간단 요약

requestsproxies={"http": url, "https": url}; urllib3 Retry를 통한 재시도 지원
httpxhttpx.Client(proxy=url); 이전의 proxies= 인수는 httpx 0.28에서 제거됨
aiohttpsession.get(url, proxy="http://USER:PASS@host:port"), 프록시 URL 내 자격 증명 포함
SOCKS5pip install "requests[socks]" 또는 httpx[socks], 이후 포트 7778에서 socks5h:// 사용
요청마다 새로운 IPkeep-alive 비활성화: Connection: close, max_keepalive_connections=0 또는 force_close=True
플로우 내 동일 IP 유지사용자명에 -session-<id>-ttl-<minutes> 포함, 최대 30분 사용
프록시 URL 스킴HTTP 포트는 HTTPS 웹사이트라도 항상 http:// 사용

requests에서 Retry-After를 준수하는 재시도형 프록시를 어떻게 사용하나요?

Session에 urllib3의 Retry가 적용된 HTTPAdapter를 마운트하세요. 연결 오류와 지정한 상태 코드를 재시도하며 지수 백오프로 대기하고, 사이트에서 429 및 503 상태와 함께 보내는 Retry-After 헤더를 존중합니다. 남은 HTTPS_PROXY 변수로 프록시가 덮어쓰기 되지 않도록 trust_env = False를 설정하세요.

requests + urllib3 Retry
import os

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

USER = os.environ.get("CRAWLRELAY_USER", "USERNAME-country-us")
PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD")
PROXY = "http://%s:%[email protected]:7777" % (USER, PASSWORD)

retry = Retry(
    total=4,
    connect=2,
    backoff_factor=1.0,  # waits of 0 s, 2 s, 4 s, 8 s (Retry-After wins on 429/503)
    status_forcelist=(429, 502, 503, 504),
    allowed_methods=frozenset({"GET", "HEAD"}),
    respect_retry_after_header=True,
)

session = requests.Session()
session.trust_env = False  # ignore HTTP(S)_PROXY from the environment
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies = {"http": PROXY, "https": PROXY}
session.headers["User-Agent"] = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
session.headers["Connection"] = "close"  # new connection per request, so usually a new exit IP

r = session.get("https://api.ipify.org", timeout=(10, 30))
print(r.status_code, r.text)

타겟 프록시 URL을 안전하게 생성하려면?

사용자명을 한 함수에서 만들고 검증하세요. 국가 코드 오타나 비인코딩 문자가 비밀번호에 있으면 자격 증명 문제처럼 보이는 407 오류가 발생할 수 있습니다. 아래 헬퍼는 문서화된 순서(USERNAME-country-{cc}-state-{state}-city-{city}-zip-{zip}-asn-{asn}-session-{id}-ttl-{minutes})를 지키고, 알 수 없는 키를 거부하며 두 자격증명을 URL 인코딩합니다.

사용자명 생성기와 프록시 URL
import re
from urllib.parse import quote

ORDER = ("country", "state", "city", "zip", "asn", "session", "ttl")
VALUE = re.compile(r"^[a-z0-9_]+$")


def build_username(base, **params):
    """build_username("USERNAME", country="us", city="new_york", session="job1", ttl=30)"""
    parts = [base]
    for key in ORDER:
        value = params.pop(key, None)
        if value is None:
            continue
        value = str(value).lower()
        if not VALUE.match(value):
            raise ValueError("bad value for %s: %r" % (key, value))
        if key == "ttl" and not 1 <= int(value) <= 30:
            raise ValueError("ttl must be 1 to 30 minutes")
        parts.append("%s-%s" % (key, value))
    if params:
        raise ValueError("unknown parameters: %s" % ", ".join(sorted(params)))
    return "-".join(parts)


def proxy_url(username, password, socks=False):
    scheme, port = ("socks5h", 7778) if socks else ("http", 7777)
    return "%s://%s:%[email protected]:%d" % (
        scheme, quote(username, safe=""), quote(password, safe=""), port)


print(proxy_url(build_username("USERNAME", country="us", state="ny", city="new_york", session="job1", ttl=20), "p@ss:word"))

httpx에서 요청마다 새 IP를 받으려면?

keep-alive를 끄세요. httpx는 기본적으로 연결을 풀링하며, 재사용된 연결은 동일한 출구 IP를 유지합니다. max_keepalive_connections=0 사용 시 각 요청이 게이트웨이에 새 연결을 열어 보통 다른 레지덴셜 IP로 나갑니다. num_bytes_downloaded는 트래픽 청구에 가장 근접한 실제 수신 바이트를 줍니다.

httpx: 회전 클라이언트
import httpx

PROXY = "http://USERNAME-country-gb:[email protected]:7777"

# No keep-alive: every request opens a new connection, so it usually gets a new exit IP
with httpx.Client(
    proxy=PROXY,
    timeout=httpx.Timeout(30.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=0),
    follow_redirects=True,
) as client:
    for _ in range(3):
        r = client.get("https://api.ipify.org")
        print(r.text, r.num_bytes_downloaded, "bytes on the wire")

asyncio로 한 사이트를 과부하하지 않고 어떻게 동시에 크롤링하나요?

전역 한도가 아닌 호스트별 동시성을 제한하세요. 전역 50 제한이면 URL 목록이 도메인별 정렬 시 모든 50개 요청이 같은 사이트에 집중됩니다. 호스트별 세마포어를 사용하면 각 사이트는 공손한 수준을 유지하고 전체 크롤링은 빠릅니다. 호스트당 2~4개부터 시작하며 동시성 가이드를 읽고 제한을 늘리세요.

httpx.AsyncClient + 호스트별 세마포어
import asyncio
from collections import defaultdict
from urllib.parse import urlsplit

import httpx

PROXY = "http://USERNAME-country-us:[email protected]:7777"
PER_HOST = 4  # parallel requests per target host
host_slots = defaultdict(lambda: asyncio.Semaphore(PER_HOST))


async def fetch(client, url):
    async with host_slots[urlsplit(url).hostname]:
        try:
            r = await client.get(url)
            return url, r.status_code, r.num_bytes_downloaded
        except httpx.HTTPError as exc:
            return url, type(exc).__name__, 0


async def main(urls):
    limits = httpx.Limits(max_connections=50, max_keepalive_connections=0)
    timeout = httpx.Timeout(30.0, connect=10.0)
    async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=timeout) as client:
        for result in await asyncio.gather(*(fetch(client, u) for u in urls)):
            print(*result)


asyncio.run(main(["https://example.com/", "https://example.org/", "https://example.net/"]))

aiohttp에서 프록시를 어떻게 사용하나요?

게이트웨이를 각 요청의 proxy=로 전달하고 프록시 URL에 자격증명을 포함하세요. aiohttp 3.x 모든 버전에서 경고 없이 작동하며, aiohttp 3.14 이후 BasicAuth과 proxy_auth= 매개변수는 사용 중단되어 4.0에서 제거됩니다. TCPConnector(force_close=True)는 응답 후 모든 연결을 닫아 요청마다 보통 새 출구 IP를 받습니다. 없으면 연결이 재사용되어 같은 IP를 잠시 유지합니다.

프록시 URL 내 자격증명과 aiohttp
import asyncio

import aiohttp

# Credentials go in the proxy URL; percent-encode a password that contains @ or :
PROXY = "http://USERNAME-country-fr:[email protected]:7777"


async def main():
    # force_close=True: one connection per request, so each request usually gets a new exit IP
    connector = aiohttp.TCPConnector(limit=20, force_close=True)
    timeout = aiohttp.ClientTimeout(total=60, sock_connect=10)
    async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
        for _ in range(3):
            async with session.get("https://api.ipify.org", proxy=PROXY) as resp:
                print(resp.status, await resp.text())


asyncio.run(main())

멀티스텝 플로우에서 하나의 IP를 유지하려면?

플로우당 세션 ID를 생성해 사용자명에 TTL이 플로우보다 길게 넣으세요. keep-alive는 괜찮습니다: 동일 출구 IP가 필요하니까요. 세션 뒤 레지덴셜 기기가 오프라인 되면 TTL 이전에 IP가 바뀔 수 있으니 플로우가 마지막 체크포인트부터 재시작 가능해야 합니다.

requests 고정 세션
import uuid

import requests

session_id = uuid.uuid4().hex[:10]  # one ID per flow, lowercase letters and digits
user = "USERNAME-country-us-state-ca-session-%s-ttl-20" % session_id
proxy = "http://%s:[email protected]:7777" % user

with requests.Session() as s:
    s.proxies = {"http": proxy, "https": proxy}
    first = s.get("https://api.ipify.org", timeout=(10, 30)).text
    second = s.get("https://api.ipify.org", timeout=(10, 30)).text
    print(first, second, "same exit" if first == second else "exit changed")

Python 크롤러는 페이지당 트래픽을 얼마나 쓰나요?

볼륨 구매 전에 100개 실제 페이지에서 측정하세요. httpx에서 num_bytes_downloaded를 로깅하거나 자체 미들웨어에서 압축된 본문 길이를 기록해 페이지 수와 곱하세요. HTML 요청만 하는 경우 보통 페이지당 수십 KB가 들고, 대역폭 가이드에서 CrawlRelay 가격 기준 1,000페이지당 비용과 수치를 참고하세요.

결과는 유지하면서 트래픽을 줄이는 세 가지 습관:

  • Accept-Encoding: gzip, br 전송 (httpx, requests 기본 gzip 압축 전송)
  • 사이트가 문서화하거나 허용 시 전체 HTML 대신 JSON 엔드포인트 요청
  • If-None-Match 또는 If-Modified-Since로 변경 없는 페이지 스킵: 304 Not Modified는 몇백 바이트만 소비

파이썬과 프록시: 궁금한 점들

requests, httpx 또는 aiohttp: 스크래핑에 어떤 걸 써야 하나요?

간단한 스크립트와 소규모 크롤에는 requests를 사용하세요. 동기와 비동기 코드에서 같은 API와 HTTP/2를 httpx[http2] 통해 사용하고 싶다면 httpx를 쓰세요. 순수 asyncio로 매우 높은 동시성을 원하면 aiohttp를 사용하세요. 스케줄링, 중복 제거, 파이프라인이 필요한 크롤은 직접 구현하지 말고 Scrapy를 쓰세요.

왜 파이썬 프록시에서 SSL: WRONG_VERSION_NUMBER 오류가 발생하나요?

프록시 주소 앞에 아마도 https://를 붙였을 겁니다. 게이트웨이 포트 7777는 일반 HTTP를 쓰고 HTTPS 사이트에는 CONNECT 터널을 열므로, 프록시 URL은 http와 https 키 모두에서 http://로 시작해야 합니다. 자세한 내용은 오류 페이지를 참고하세요.

파이썬에서 SOCKS5 프록시는 어떻게 쓰나요?

추가 패키지(pip install "requests[socks]" 또는 pip install "httpx[socks]")를 설치한 뒤 socks5h://USERNAME:[email protected]:7778를 사용하세요. h는 호스트네임을 프록시로 보내서 DNS 확인이 종료 지점에서 이뤄지고, 지역별 방문자와 같은 응답을 받게 됩니다.

회전 프록시가 같은 IP를 두 번 반환하는 이유는?

클라이언트가 keep-alive 연결을 재사용했기 때문이며, 회전은 새 연결당 이뤄집니다. requests에서는 Connection: close을 보내고, httpx에선 max_keepalive_connections=0을 설정하거나 aiohttp에선 force_close=True를 사용하세요. 사용자 이름에 session- 부분이 있으면 일부러 한 IP를 고정하는 것이므로 확인해보세요.

프록시 요청 타임아웃은 어떻게 설정하나요?

명시적으로 설정하세요: requests는 기본 타임아웃이 없습니다. 연결 타임아웃은 약 10초, 읽기 타임아웃은 30초가 HTML 페이지에 적합하며, 모바일 단말 및 느린 대상에는 45~60초가 필요합니다. 타임아웃이 발생하면 더 오래 기다리기보다 새 연결에서 한 번만 재시도하세요. 읽기 타임아웃도 참고하세요.

이 프록시들을 Jupyter나 pandas와 함께 써도 되나요?

네. requests나 httpx를 사용하는 모든 것에 동일한 프록시 설정을 적용할 수 있습니다. URL에 pandas.read_html 또는 read_csv이 있을 경우, 먼저 프록시를 쓴 requests 호출로 콘텐츠를 다운로드한 뒤 텍스트나 바이트를 pandas에 넘기세요.

레지덴셜 IP를 통해 파이썬 스크레이퍼 실행하기

US$1.50는 1 GB용, US$0.50/GB는 50 GB에서 제공합니다.

US$0.50/GB 부터 · KYC 없이

시작하기