Socks5Proxy

Python requests 프록시 설정, SOCKS5와 HTTP 프록시 연결하기

이 가이드는 Python requests 라이브러리에 Socks5Proxy의 HTTP 프록시와 SOCKS5 프록시를 연결하는 방법, 아이디·비밀번호 인증, 목록 순환과 재시도 코드를 단계별로 설명해요.

준비할 것은 무엇인가요?

Python 3.8 이상과 requests 라이브러리가 필요해요. SOCKS5를 쓰려면 SOCKS 지원 묶음을 함께 설치해야 해요. requests[socks]를 설치하면 PySocks가 같이 설치되어 socks5와 socks5h 주소를 쓸 수 있어요.

프록시 정보는 대시보드의 내 프록시 화면에서 확인해요. 이 예시에서는 IP 198.51.100.23, HTTP 포트 8000, SOCKS5 포트 1080, 아이디 spx_user, 비밀번호 spx_pass를 써요. 실제 값으로 바꿔 넣으세요.

pip install "requests[socks]"

HTTP 프록시는 어떻게 연결하나요?

requests는 proxies 사전에 주소를 넣으면 그 프록시로 요청을 보내요. http와 https 두 키에 같은 HTTP 프록시 주소를 넣는 것이 중요해요. https 키가 없으면 HTTPS 주소 요청이 프록시를 거치지 않고 바로 나가요.

HTTPS 사이트 요청은 CONNECT 방식으로 프록시를 통과해서 내용은 처음부터 끝까지 암호화된 채 전달돼요. 아래 코드는 IP 확인 주소로 요청해 응답에 프록시 IP가 보이는지 확인하는 예시예요.

import requests

proxy = "http://spx_user:spx_pass@198.51.100.23:8000"
proxies = {"http": proxy, "https": proxy}

r = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=20)
print(r.status_code, r.json())  # the "ip" field should show the proxy IP

SOCKS5 프록시는 어떻게 연결하나요?

SOCKS5는 주소 앞을 socks5h로 쓰는 것을 추천해요. socks5h는 도메인 이름 확인을 프록시 쪽에서 해서 내 서버의 DNS 요청이 밖으로 드러나지 않고, 프록시가 있는 나라 기준으로 이름이 확인돼요. socks5로 쓰면 이름 확인을 내 서버에서 한 뒤 IP만 프록시로 보내요.

포트는 SOCKS5 포트를 써야 해요. HTTP 포트에 SOCKS5 주소를 넣으면 연결이 되지 않으니 줄마다 있는 두 포트를 구분해 주세요.

import requests

proxy = "socks5h://spx_user:spx_pass@198.51.100.23:1080"
proxies = {"http": proxy, "https": proxy}

r = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=20)
print(r.json())

여러 프록시를 순환하며 요청하려면 어떻게 하나요?

많은 페이지를 수집할 때는 목록을 받아 요청마다 다음 프록시를 쓰는 방식이 기본이에요. 아래 코드는 API로 SOCKS5 목록을 받아 순서대로 돌아가며 쓰고, 실패한 요청은 다른 프록시로 최대 3번 다시 보내요. 같은 도메인에 너무 빠르게 요청하지 않도록 잠깐 쉬는 시간도 넣었어요.

실패한 프록시를 바로 버리기보다 잠시 쉬게 했다가 다시 쓰는 편이 좋아요. 일시적인 오류는 대상 사이트의 상태 때문인 경우가 많아요.

import itertools
import time
import requests

API = "https://socks5proxy.io/api/v1"
KEY = "YOUR_API_KEY"

lines = requests.get(
    API + "/proxies/download",
    params={"format": "user:pass@ip:port", "protocol": "socks5"},
    headers={"Authorization": "Bearer " + KEY},
    timeout=30,
).text.split()
pool = itertools.cycle(lines)

def fetch(url, tries=3):
    for _ in range(tries):
        p = "socks5h://" + next(pool)
        try:
            r = requests.get(url, proxies={"http": p, "https": p}, timeout=20)
            if r.status_code == 200:
                return r.text
        except requests.RequestException:
            pass
        time.sleep(1)
    return None

for page in range(1, 6):
    html = fetch("https://example.com/list?page=" + str(page))
    print(page, "ok" if html else "failed")
    time.sleep(0.5)

세션과 쿠키를 유지하려면 어떻게 하나요?

로그인이나 페이지 넘기기처럼 쿠키가 이어져야 하는 흐름은 requests.Session 하나에 프록시 하나를 고정해서 쓰세요. 세션 객체의 proxies 속성에 주소를 넣으면 그 세션의 모든 요청이 같은 IP로 나가요. 흐름이 끝나면 세션을 닫고 다음 프록시로 새 세션을 만들면 돼요.

세션마다 User-Agent와 Accept-Language 헤더도 정해 두면 대상 사이트에서 보이는 모습이 일정해져요.

import requests

s = requests.Session()
s.proxies = {
    "http": "http://spx_user:spx_pass@198.51.100.23:8000",
    "https": "http://spx_user:spx_pass@198.51.100.23:8000",
}
s.headers.update({"User-Agent": "Mozilla/5.0", "Accept-Language": "en-US,en;q=0.9"})

s.get("https://example.com/", timeout=20)          # cookies are stored in the session
r = s.get("https://example.com/account", timeout=20)  # same IP, same cookies
print(r.status_code)
s.close()

IP 인증으로 연결할 때는 어떻게 쓰나요?

대시보드에 이 코드를 실행하는 서버의 IPv4를 등록하면 주소에서 아이디와 비밀번호를 빼고 쓸 수 있어요. 비밀번호를 코드나 설정 파일에 두지 않아도 되어 여러 서버에 같은 코드를 배포하기 쉬워요. 목록을 받을 때도 format을 ip:port로 고르면 돼요.

import requests

proxy = "http://198.51.100.23:8000"  # no user or password, server IP is authorized in the dashboard
r = requests.get("https://api.ipify.org", proxies={"http": proxy, "https": proxy}, timeout=20)
print(r.text)

여러 요청을 동시에 빠르게 보내려면 어떻게 하나요?

requests는 요청 하나가 끝날 때까지 기다리는 방식이라, 많은 페이지를 빨리 받으려면 여러 작업을 동시에 돌려야 해요. 가장 쉬운 방법은 concurrent.futures의 ThreadPoolExecutor예요. 작업 수를 프록시 수보다 적게 두면 IP 하나에 요청이 몰리지 않아요.

아래 코드는 작업자 20개로 주소 목록을 나눠 받고, 작업마다 목록에서 다음 프록시를 골라 써요. itertools.cycle은 여러 스레드에서 함께 써도 다음 값을 꺼내는 데 문제가 없지만, 정확한 순서가 필요하다면 잠금을 함께 쓰세요.

import itertools
from concurrent.futures import ThreadPoolExecutor
import requests

lines = open("proxies.txt").read().split()  # user:pass@ip:port per line
pool = itertools.cycle(lines)

def get(url):
    p = "http://" + next(pool)
    try:
        return url, requests.get(url, proxies={"http": p, "https": p}, timeout=20).status_code
    except requests.RequestException as e:
        return url, type(e).__name__

urls = ["https://example.com/item/" + str(i) for i in range(200)]
with ThreadPoolExecutor(max_workers=20) as ex:
    for url, result in ex.map(get, urls):
        print(url, result)

httpx나 aiohttp 같은 비동기 라이브러리도 쓸 수 있나요?

네. httpx는 클라이언트를 만들 때 proxy 인자에 주소를 넣으면 되고, SOCKS5는 httpx[socks]를 설치하면 socks5 주소를 쓸 수 있어요. 비동기 클라이언트 AsyncClient도 같은 인자를 받아요.

aiohttp는 요청마다 proxy 인자에 HTTP 프록시 주소를 넣고, proxy_auth에 aiohttp.BasicAuth를 넘겨요. aiohttp는 기본으로 SOCKS를 지원하지 않아서 SOCKS5가 필요하면 aiohttp-socks 패키지의 커넥터를 써야 해요.

비동기 방식은 요청 수천 개를 적은 자원으로 동시에 처리할 수 있지만, 동시 요청 수를 제한하지 않으면 대상 사이트와 프록시에 한꺼번에 몰려요. asyncio.Semaphore로 동시에 진행할 요청 수를 정해 두세요.

import httpx

with httpx.Client(proxy="http://spx_user:spx_pass@198.51.100.23:8000", timeout=20) as client:
    print(client.get("https://api.ipify.org").text)

자주 나는 오류는 어떻게 해결하나요?

Missing dependencies for SOCKS support 오류는 requests[socks]가 설치되지 않았다는 뜻이에요. 같은 가상 환경에 다시 설치해 주세요.

407 Proxy Authentication Required는 아이디나 비밀번호가 틀렸거나, IP 인증을 쓰는데 서버 IP가 등록되지 않은 경우예요. 비밀번호에 특수 문자가 있다면 urllib.parse.quote로 바꿔 넣어야 해요.

연결 시간 초과는 포트가 맞지 않는 경우가 가장 많아요. HTTP 주소에는 HTTP 포트를, socks5h 주소에는 SOCKS5 포트를 썼는지 확인하세요. timeout 값을 꼭 넣어 두면 응답 없는 요청이 작업 전체를 멈추게 하는 일을 막을 수 있어요.

  • SOCKS 오류: pip install "requests[socks]"
  • 407: 아이디·비밀번호 또는 등록한 IP 확인
  • 시간 초과: HTTP 포트와 SOCKS5 포트 구분
  • 모든 요청에 timeout 넣기
Python requests 프록시 설정, SOCKS5·HTTP 연결 | Socks5Proxy