Python Requests 프록시: 설정, 인증, IP 로테이션 가이드
python requests proxy 설정은 거의 모든 스크래핑이나 모니터링 작업에서 첫 번째 실질적인 단계이며, 요청이 조용히 자신의 IP를 되돌려 주거나 설명할 수 없는 인증 오류가 날 때까지는 사소해 보이는 부류의 작업입니다. 이 가이드가 그 문제를 해결합니다. 끝날 무렵이면 requests 라이브러리를 HTTP, HTTPS, SOCKS5로 프록시를 통해 라우팅하는 복사해서 붙여넣는 코드, 인증 추가, 세션으로 연결 재사용, 타임아웃과 재시도 설정, 풀을 통한 로테이션, IP가 실제로 바뀌었는지 검증, 그리고 실제로 마주칠 오류 처리까지 갖추게 됩니다. 이 글 전체에서의 정직한 활용 사례는 정당한 업무, 즉 공개 데이터 스크래핑, 가격과 SERP 모니터링, SEO 리서치이며, 로그인 뒤에 있는 것이 아닙니다.
마지막 업데이트: 2026년 8월 24일. 이 글에는 제휴 링크가 포함되어 있습니다. 저희 제휴 고지를 참고하세요. 공급업체의 요금제와 엔드포인트는 바뀌므로, 어떤 수치든 의존하기 전에 공급업체 자체 사이트에서 확인하세요.
Python requests에서 프록시를 설정하는 방법
가장 작은 동작 예시는 http와 https 키를 가진 딕셔너리로, 어떤 요청 메서드의 proxies 인자에든 전달합니다.
import requests
proxies = {
"http": "http://198.51.100.10:8080",
"https": "http://198.51.100.10:8080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())
여기서 사람들이 걸려 넘어지는 지점이 두 가지 있습니다. 첫째, 값은 맨 IP가 아니라 완전한 프록시 URL입니다. 스킴(http://), 호스트, 포트가 필요합니다. 둘째, 딕셔너리 키는 프록시의 프로토콜이 아닙니다. 목적지 URL의 스킴에 따라 어떤 프록시를 쓸지 고르는 것입니다. https:// 사이트로의 요청은 https 키를 씁니다. 이 구분은 다음 섹션에서 중요해지며, 프록시가 "작동하지 않는" 가장 흔한 원인이기도 합니다.
위처럼 항상 timeout을 전달하세요. 프록시는 여러분과 대상 사이에 있으므로, 죽었거나 느린 프록시는 이것이 없으면 스크립트를 영원히 멈추게 합니다.
HTTP, HTTPS, SOCKS5 프록시
proxies 딕셔너리의 키는 목적지 스킴으로 라우팅하고, 값의 스킴은 프록시 자체가 말하는 언어입니다. 따라서 HTTPS 트래픽을 터널링할 수 있는 단일 HTTP 프록시는 두 키 모두 아래에 http:// 값으로 씁니다. 이것이 바로 대부분의 데이터센터 및 주거용 HTTP 프록시의 실제 모습입니다. HTTPS 사이트를 스크래핑한다고 해서 https:// 값이 필요한 것은 아닙니다.
SOCKS5는 다릅니다. 추가 의존성이 필요합니다.
pip install "requests[socks]"
그런 다음 socks5h:// 스킴을 씁니다. 끝의 h가 중요합니다. 호스트명을 로컬에서 해석하는 대신 DNS 해석을 프록시를 통해 보내므로, 스크래핑에서는 여러분 머신의 DNS가 대상을 누출하지 않도록 거의 항상 이것을 원하게 됩니다.
proxies = {
"http": "socks5h://user:pass@proxy-host:1080",
"https": "socks5h://user:pass@proxy-host:1080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())
| 스킴 접두사 | 하는 일 |
|---|---|
http:// | HTTP 프록시를 통해 라우팅. HTTPS 대상은 CONNECT로 터널링. 대부분 프록시의 기본값. |
socks5:// | SOCKS5를 통해 라우팅하지만, DNS는 여러분 머신에서 로컬로 해석. |
socks5h:// | SOCKS5를 통해 라우팅하고 DNS를 프록시에서 해석(h의 의미). 스크래핑에 권장. |
Python에서의 프록시 인증
대부분의 유료 프록시는 사용자 이름과 비밀번호를 요구합니다. requests에서는 자격 증명이 http://user:pass@host:port처럼 프록시 URL 안으로 곧장 들어갑니다.
proxy_url = "http://my-username:my-password@proxy-host:8080"
proxies = {"http": proxy_url, "https": proxy_url}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())
여기 사람들의 오후를 통째로 잡아먹는 버그가 있습니다. 비밀번호에 @, :, #, / 같은 문자가 들어 있으면 URL 파싱이 깨지고 뚜렷한 이유 없이 인증이 실패합니다. URL을 만들기 전에 urllib.parse.quote로 사용자 이름과 비밀번호를 URL 인코딩하세요.
from urllib.parse import quote
user = "my-username"
password = "p@ss:w#rd" # contains reserved characters
host, port = "proxy-host", 8080
proxy_url = f"http://{quote(user, safe='')}:{quote(password, safe='')}@{host}:{port}"
proxies = {"http": proxy_url, "https": proxy_url}
safe='' 인자는 일부를 손대지 않고 남기는 대신 모든 예약 문자를 인코딩하도록 quote에 지시합니다. 처음부터 이렇게 해두면 "이유 없이 인증이 실패한다"는 유령 같은 버그를 쫓을 일이 결코 없습니다. 그래도 프록시가 여러분을 거부한다면, 주목할 상태 코드는 407 Proxy Authentication Required입니다. 아래 오류 섹션에서 다룹니다.
requests.Session으로 연결 재사용하기
일회성 요청을 넘어서는 무엇이든 Session을 쓰세요. 세션에 프록시를 한 번 설정하면 그것을 통과하는 모든 요청이 프록시, 쿠키 저장소, 하부의 TCP 연결을 재사용합니다.
session = requests.Session()
session.proxies = {
"http": proxy_url,
"https": proxy_url,
}
session.get("https://httpbin.org/ip", timeout=10)
session.get("https://httpbin.org/headers", timeout=10) # same proxy, same cookies
이것은 스크래핑에서 두 가지로 중요합니다. 쿠키가 유지되므로 로그인이나 세션 상태가 요청을 가로질러 이어지고, 연결 재사용은 호출마다의 핸드셰이크 오버헤드를 줄여 주는데, 같은 프록시를 통해 수천 건의 요청을 보낼 때 이는 실질적인 속도 이득입니다.
환경 변수로 프록시 설정하기
requests는 환경에서 프록시 설정을 자동으로 읽으므로, 자격 증명을 코드에서 완전히 떼어 둘 수 있습니다.
export HTTP_PROXY="http://user:pass@proxy-host:8080"
export HTTPS_PROXY="http://user:pass@proxy-host:8080"
대문자와 소문자 이름(http_proxy, https_proxy) 둘 다 인식됩니다. 이 방식이 더 깔끔한 경우에 대한 몇 가지 메모입니다.
- 코드를 건드리지 않고 환경별(dev, staging, production)로 프록시를 전환하기에 좋습니다.
- 비밀 정보를 리포지토리가 아니라
.env파일이나 CI 시크릿 저장소에 두기에 좋습니다. - 한 세션에서 환경 프록시를 무시하려면
session.trust_env = False를 설정하세요. 머신 전역 프록시가 설정되어 있어도 특정 스크립트만 직접 나가게 하거나 자체 목록을 쓰게 하고 싶을 때 편리합니다.
타임아웃과 재시도 추가하기
이 부분은 대부분의 튜토리얼이 건너뛰는데, 새벽 3시에 죽는 스크립트와 끝까지 완주하는 스크립트의 차이를 만듭니다. 프록시는 직접 연결보다 불안정해서 IP가 일시적으로 과부하되거나 레이트 리밋을 받을 수 있으므로, 여기서는 재시도가 평소보다 더 중요합니다.
진짜 timeout부터 시작하세요. 튜플을 전달하면 연결용과 읽기용 타임아웃으로 나뉘어, 느린 프록시는 연결에서 빠르게 실패하되 느린 응답은 끝까지 마칠 수 있습니다.
response = requests.get(url, proxies=proxies, timeout=(5, 20)) # (connect, read) seconds
그런 다음 urllib3의 Retry와 HTTPAdapter를 써서 세션에 재시도 전략을 마운트합니다. backoff_factor는 시도 간격을 벌려 힘겨워하는 엔드포인트를 두들기지 않게 하고, status_forcelist는 일시적인 서버 오류와 레이트 리밋 코드에서 재시도합니다.
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=5,
backoff_factor=1, # waits 0s, 2s, 4s, 8s... between tries
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"],
)
session = requests.Session()
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
session.proxies = {"http": proxy_url, "https": proxy_url}
response = session.get("https://httpbin.org/ip", timeout=(5, 20))
print(response.json())
더 오래된 urllib3 릴리스에서는 인자 이름이 allowed_methods가 아니라 method_whitelist입니다. 여기에 걸리면 라이브러리를 업데이트하세요. Retry는 연결 오류와 여러분이 나열한 상태 코드를 처리한다는 점에 유의하세요. 그저 차단 페이지를 반환한 깔끔한 200은 재시도하지 않습니다. 그래서 아래의 로테이션과 오류 섹션이 여전히 중요합니다.
풀을 통해 프록시 로테이션하기
모든 요청을 한 IP에서 보내는 것은 레이트 리밋이나 차단을 당하는 가장 빠른 길입니다. 로테이션은 요청을 여러 IP에 분산시킵니다. 정직한 접근이 두 가지 있는데, 각각 매우 다른 규모에서 수고에 값합니다. 개념부터 알고 싶다면 로테이팅 프록시가 작동하는 방식에 대한 저희 설명을 보세요.
방법 A - random.choice를 쓴 수동 풀
정적 IP가 한 줌 있다면 리스트에 담아 요청마다 하나씩 고르세요. random.choice는 간단한 무작위 선택을 제공합니다.
import random
import requests
proxy_pool = [
"http://user:pass@ip-1:8080",
"http://user:pass@ip-2:8080",
"http://user:pass@ip-3:8080",
]
targets = ["https://httpbin.org/ip"] * 6
for url in targets:
proxy = random.choice(proxy_pool)
proxies = {"http": proxy, "https": proxy}
try:
r = requests.get(url, proxies=proxies, timeout=10)
print(proxy, "->", r.json()["origin"])
except requests.exceptions.RequestException as exc:
print(f"failed via {proxy}: {exc}")
무작위 대신 엄격한 라운드 로빈을 원하면 itertools.cycle을 쓰고 매번 next(proxy_cycle)을 호출하세요. 방법 A는 이미 소유한 작고 고정된 IP 집합에는 괜찮습니다. 그 대가는 여러분이 목록을 유지하고, 죽은 IP를 솎아내고, 대상이 어떤 것을 차단하기 시작했는지 추적해야 한다는 점입니다.
방법 B - 공급업체의 로테이팅 엔드포인트
규모가 커지면 목록 유지를 멈추고 모든 요청을 하나의 백본 엔드포인트로 향하게 하여, 출구 IP 교체를 공급업체에 맡깁니다. Webshare의 실제 로테이팅 엔드포인트 형식을 쓰면, 사용자 이름에 -rotate를 붙이고 모든 것을 80 포트의 p.webshare.io로 보냅니다.
import requests
# One endpoint; the provider hands you a fresh exit IP per request.
proxy = "http://<username>-rotate:<password>@p.webshare.io:80/"
proxies = {"http": proxy, "https": proxy}
for _ in range(6):
r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(r.json()["origin"]) # typically a different IP each call
-rotate 접미사는 호출마다 새 IP를 요청합니다. 다단계 흐름(스티키 세션)에서 같은 IP를 지속시켜야 한다면, 공급업체는 대신 숫자 세션 ID 형식을 제공합니다. 이것들은 바뀔 수 있으므로, 의존하기 전에 정확한 엔드포인트와 사용자 이름 형식을 help.webshare.io에서 확인하세요. 방법 B의 요점은 단순합니다. 방법 A의 유지 부담을 통째로 없앤다는 것이며, 그래서 IP가 몇 개를 넘어가면 이 방식이 이깁니다.
요금제에 얽매이지 않고 위 스니펫들을 실제로 돌려보려면, Webshare의 무료 등급이 월 10개의 프록시와 1GB의 대역폭을 제공합니다(2026년 8월 기준, 사이트에서 확인). 로테이션, 인증, 재시도 코드를 처음부터 끝까지 시험하기에 충분합니다. 여기에서 무료 프록시를 받아 위 예제에 여러분의 진짜 자격 증명을 붙여 넣으세요.
제 사용 경험에서 나온 작은 정직한 메모입니다. 위 스니펫은 요청마다 로테이션하지만, 제 스크래핑에서는 실제로 매 호출마다 새 IP를 받는 대신 스티키 세션을 고정하며, 작업이 도는 내내 같은 출구 IP를 유지했습니다. Webshare 엔드포인트를 통해 그것을 설정하기는 간단했고, 407 인증 오류나 429 레이트 오류에 부딪히지 않았기에 그것들을 우회하려고 무거운 요청 간격이 필요한 적은 한 번도 없었습니다. 그렇지만 결과는 대상마다 다르니, 작업을 확장하기 전에 여러분 자신의 URL에 대해 성공률을 측정하세요.
프록시 IP 검증하기
이 중 무엇이든 신뢰하기 전에, 프록시가 실제로 경로에 있는지 확인하세요. IP 에코 서비스를 프록시가 있을 때와 없을 때 모두 호출해 비교합니다. 둘이 일치하면 프록시가 쓰이지 않는 것입니다.
import requests
def show_ip(proxies=None):
r = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10)
return r.json()["ip"]
print("direct: ", show_ip())
print("via proxy:", show_ip({"http": proxy_url, "https": proxy_url}))
"via proxy" 줄이 여전히 여러분의 진짜 IP를 보여준다면, 흔한 원인은 https:// URL을 요청하는데 딕셔너리에 https 키가 빠져 있어 요청이 조용히 직접 나간 것입니다. https 항목을 추가하고(설정 섹션 참고) 다시 실행하세요. api.ipify.org는 https://httpbin.org/ip로 바꿀 수 있습니다. 다만 ip 대신 origin 필드를 읽으면 됩니다.
프록시 오류 처리하기
요청을 try/except로 감싸고 특정 예외를 잡으면, 실행 전체를 무너뜨리는 대신 올바르게 대응할 수 있습니다.
import requests
try:
r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
r.raise_for_status()
print(r.json())
except requests.exceptions.ProxyError as exc:
print("proxy dead or blocked:", exc)
except requests.exceptions.ConnectTimeout:
print("proxy did not answer in time")
except requests.exceptions.HTTPError as exc:
print("target returned an error status:", exc.response.status_code)
except requests.exceptions.RequestException as exc:
print("request failed:", exc)
가장 구체적인 것부터 가장 일반적인 것으로 잡으세요. RequestException은 기반 클래스이므로 모든 것을 받아내는 마지막 자리에 두어야 합니다. 흔한 실패를 해독하는 방법은 다음과 같습니다.
| 증상 | 가능한 원인 | 해결 |
|---|---|---|
| "Max retries exceeded" / ProxyError | 죽었거나, 잘못됐거나, 차단된 프록시 엔드포인트 | 호스트/포트 확인, 다른 IP로 로테이션, 프록시가 살아 있는지 확인 |
| HTTP 407 | 프록시 인증 거부 | 사용자 이름/비밀번호 수정. quote로 비밀번호를 URL 인코딩 |
| HTTP 429 | 대상에 의한 레이트 리밋 | 속도를 늦추고, 백오프를 더하고, IP를 로테이션하고, 요청 간격을 벌리기 |
| HTTP 403 | 대상이 이 IP나 핑거프린트를 차단 | IP 로테이션, 더 은밀한 프록시 유형 시도, 요청 헤더 검토 |
| ConnectTimeout | 프록시는 살아 있으나 느리거나 과부하 | 연결 타임아웃을 올리고, 재시도하고, 느린 IP를 풀에서 제거 |
책임 있게 스크래핑하기
프록시는 정당한 업무를 위한 도구입니다. 공개 데이터 스크래핑, 가격과 SERP 모니터링, 광고 검증, 시장 조사, SEO 데이터 등입니다. 깨끗하게 유지하세요. 각 사이트의 robots.txt와 이용 약관을 존중하고, 공개적으로 이용 가능한 데이터만 대상으로 하고, 남의 서버를 저하시키지 않도록 요청 속도를 조절하고, 로그인 뒤에 있는 것에서는 멀리 떨어지세요. 이것은 일반적인 좋은 관행이지 법률 자문이 아닙니다. 여러분 자신의 프로젝트와 관할권에 적용되는 규칙을 확인하세요. 이것을 잘 해내는 큰 그림은 웹 스크래핑용 프록시 가이드를 보세요.
자주 묻는 질문
requests 라이브러리는 SOCKS5를 지원하나요?
네, pip install "requests[socks]"로 추가분을 설치하면 됩니다. 그런 다음 proxies 딕셔너리에 socks5h:// 값을 쓰면 DNS가 프록시를 통해 해석됩니다.
왜 제 요청은 여전히 진짜 IP를 보여주나요?
거의 항상 HTTPS URL을 요청하는데 https 키가 빠져 요청이 직접 나갔기 때문입니다. http와 https 키가 모두 설정되었는지 확인한 뒤, 위의 검증 스니펫을 다시 실행하세요.
무료 프록시는 Python 스크래핑에 괜찮은가요?
코드 학습용이라면 괜찮습니다. 실제 작업에서는 느리고, 수명이 짧고, 흔히 이미 차단되었거나 안전하지 않습니다. 유료 공급업체의 진짜 무료 등급(제한적이지만 신뢰할 수 있는 IP)이 물량에 돈을 쓰기 전에 바로 그 코드를 시험하는 더 나은 방법입니다.
요청마다 새 IP를 얻으려면 어떻게 하나요?
random.choice로 직접 풀을 로테이션하거나(방법 A), <username>[email protected]:80 같은 공급업체의 로테이팅 엔드포인트로 모든 요청을 향하게 하면(방법 B), 후자는 출구 IP를 여러분을 위해 교체합니다.
같은 프록시 설정을 httpx나 aiohttp에서도 쓸 수 있나요?
개념은 이어지지만 구문이 다릅니다. httpx와 aiohttp는 같은 딕셔너리가 아니라 프록시 URL이나 클라이언트별 설정을 받습니다. 명령줄에 대해서는 저희 curl로 프록시 쓰기 가이드를, 큰 그림에 대해서는 웹 스크래핑용 프록시 개요를 보세요.
결론
경로는 언제나 같습니다. proxies 딕셔너리를 만들고, 인증을 추가하고(비밀번호를 URL 인코딩), Session을 재사용하고, 타임아웃과 Retry 어댑터를 설정한 뒤, 로테이션합니다. IP 몇 개에는 수동 풀, 규모가 커지면 공급업체의 로테이팅 엔드포인트입니다. IP가 바뀌었는지 검증하고, 오류를 잡고, 각 사이트의 규칙 안에서 공개 데이터만 스크래핑하세요. 공급업체를 고를 때는 최고의 프록시 서비스 정리가 선택지를 정직하게 비교합니다. 먼저 아무것도 쓰지 않고 위의 모든 것을 돌려보려면, Webshare의 무료 프록시를 받아 자격 증명을 스니펫에 넣고, 규모를 키우기 전에 각 블록이 여러분 자신의 대상에 대해 작동하는지 확인하세요.