Проксі в Python: повний посібник із прикладами коду
Повний посібник із прикладами коду для використання проксі в Python.
Бібліотека requests у Python дає змогу легко спрямовувати трафік через проксі. Цей посібник починається з базового запиту й поступово переходить до сесій, SOCKS5, тайм-аутів, повторних спроб, httpx, паралельних запитів і помилок, які трапляються найчастіше.
У всіх прикладах використано заповнювачі: замініть USERNAME, PASSWORD, HOST і PORT даними свого проксі. Код працює з актуальними версіями Python 3, requests і httpx.
- Використовуйте один URL проксі з
http://для трафікуhttpіhttpsта кодуйте логін і пароль відсотковим кодуванням. - Завжди задавайте тайм-аут: за замовчуванням
requestsчекає нескінченно. - Використовуйте одну
Sessionдля багатьох запитів, а для SOCKS5 —socks5h://, щоб DNS розв’язував проксі. - Повторюйте лише тимчасові помилки з наростаючою затримкою та тримайте паралельні запити в межах лімітів вашого тарифу.
- Зберігайте облікові дані у змінних середовища й ніколи не виводьте URL проксі.
1.Що потрібно перед початком
Вам потрібні Python 3.9 або новіший, бібліотека requests і дані вашого проксі: хост, порт, логін, пароль і протокол (HTTP(S) або SOCKS5). Встановіть бібліотеку за допомогою pip:
python -m pip install requestsURL проксі має вигляд scheme://username:password@host:port. Для HTTP(S)-проксі схема — http. Символи /, ?, #, %, @ і : мають особливе значення в URL, тому логін і пароль потрібно закодувати відсотковим кодуванням. Це вміє стандартна бібліотека:
from urllib.parse import quote
username = "USERNAME"
password = "p@ss/word#1" # a password with special characters
proxy = f"http://{quote(username, safe='')}:{quote(password, safe='')}@HOST:PORT"
# -> http://USERNAME:p%40ss%2Fword%231@HOST:PORT2.Базовий запит через проксі в Python requests
Передайте URL проксі для http і https. Замініть заповнювачі власними даними.
import requests
proxy = "http://USERNAME:PASSWORD@HOST:PORT"
proxies = {"http": proxy, "https": proxy}
r = requests.get("https://example.com", proxies=proxies, timeout=15)
print(r.status_code)Схема в URL проксі вказує, як ви спілкуєтеся з проксі, а не який сайт відкриваєте. URL проксі з http:// обслуговує і сторінки http://, і https://. Для HTTPS-сторінки requests просить проксі відкрити тунель, і зашифроване з’єднання із сайтом проходить крізь нього без змін.
Перевірте, яку IP-адресу бачить сайт
Наш публічний ендпоінт повертає IP-адресу й країну, які бачить наш сервер для запиту. Дозволено до 30 запитів на хвилину з однієї адреси, нічого не зберігається.
r = requests.get("https://xameleon.cloud/api/tools/ip", proxies=proxies, timeout=15)
print(r.json()) # {'ip': '203.0.113.25', 'country': 'UA'}Якщо адреса належить оператору, а не вам, проксі використовується.
3.Не зберігайте облікові дані в коді
Не вписуйте логін і пароль безпосередньо в код. Читайте їх зі змінних середовища, щоб вони не потрапляли в репозиторій, журнали й скриншоти.
import os
from urllib.parse import quote
user = quote(os.environ["PROXY_USER"], safe="")
password = quote(os.environ["PROXY_PASS"], safe="")
proxy = f"http://{user}:{password}@{os.environ['PROXY_HOST']}" # PROXY_HOST is HOST:PORT
proxies = {"http": proxy, "https": proxy}Задайте змінні в оболонці (export PROXY_USER=... у Linux і macOS, $env:PROXY_USER = "..." у PowerShell) або завантажуйте їх із файлу .env, який виключено з системи контролю версій. Ніколи не виводьте й не записуйте в журнал URL проксі: у ньому є ваш пароль. Докладніше — у матеріалі про безпечне та відповідальне використання проксі.
4.Використовуйте сесію
Сесія повторно використовує з’єднання, що швидше, коли ви надсилаєте багато запитів.
import requests
urls = ["https://example.com/page/1", "https://example.com/page/2"]
with requests.Session() as session:
session.proxies.update(proxies)
session.headers.update({"User-Agent": "my-app/1.0 (contact: [email protected])"})
for url in urls:
r = session.get(url, timeout=15)
print(url, r.status_code)Сесія також зберігає cookie між запитами, а оскільки IP стабільний, кожен запит у ній виходить з тієї самої адреси, поки ви продовжуєте працювати. Блок with закриває з’єднання, коли ви закінчуєте.
Змінні середовища можуть перевизначити проксі сесії
requests також читає HTTP_PROXY, HTTPS_PROXY і NO_PROXY зі змінних середовища. Якщо вони задані, вони мають пріоритет над session.proxies (але не над proxies=, переданим у сам виклик), тому сесія може непомітно скористатися не тим проксі. Передавайте proxies= у кожен запит або вимкніть середовище для сесії:
session.trust_env = False # ignore HTTP_PROXY, HTTPS_PROXY and NO_PROXYtrust_env = False також змушує requests ігнорувати REQUESTS_CA_BUNDLE і .netrc, тож використовуйте його лише там, де ви на них не покладаєтеся.
5.SOCKS5 проксі в Python
Для SOCKS5 встановіть додатковий пакет і змініть схему в URL проксі.
python -m pip install "requests[socks]"proxy = "socks5h://USERNAME:PASSWORD@HOST:PORT"
proxies = {"http": proxy, "https": proxy}
r = requests.get("https://example.com", proxies=proxies, timeout=15)
print(r.status_code)Літера h у socks5h має значення. У socks5:// ваш комп’ютер сам шукає IP-адресу сайту й передає проксі лише число. У socks5h:// до проксі йде ім’я сайту, і розв’язує його вже проксі. Віддавайте перевагу socks5h://: так немає локальних DNS-запитів, і працюють імена, яких ваш комп’ютер не може розв’язати.
Для вебзапитів використовуйте HTTP(S). Обирайте SOCKS5, коли ваш інструмент працює із серверами, що не є сайтами, або підтримує лише SOCKS.
6.Тайм-аути та повторні спроби для проксі в Python
Завжди задавайте тайм-аут і повторюйте тимчасові помилки з наростаючою затримкою.
Тайм-аути
У requests немає тайм-ауту за замовчуванням, тому завислий зв’язок може зупинити вашу програму назавжди. Задавайте timeout у кожному виклику. Одне число діє і для підключення, і для читання, а кортеж задає їх окремо:
# (connect timeout, read timeout) in seconds
r = session.get(url, timeout=(5, 20))Тайм-аут підключення охоплює з’єднання з проксі та відкриття каналу. Тайм-аут читання — це час очікування наступних байтів відповіді. Стільникові канали додають затримку, тому починайте з щедрих значень і підбирайте їх за власними вимірюваннями.
Повторні спроби з наростаючою затримкою
Повторюйте лише тимчасові проблеми: помилки з’єднання та відповіді на кшталт 429, 502, 503 і 504. Не повторюйте помилки, які самі не зникнуть, як-от 404 чи 407. Бібліотека urllib3 може повторювати запити за вас і чекати довше після кожної невдалої спроби:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=3,
backoff_factor=1, # wait longer after each failed attempt
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "HEAD"], # retry only requests that are safe to repeat
respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry)
session = requests.Session()
session.proxies.update(proxies)
session.mount("http://", adapter)
session.mount("https://", adapter)Заголовок Retry-After береться до уваги, а allowed_methods обмежує повтори запитами, які безпечно повторювати. Коли спроби вичерпано, requests викликає RetryError.
Обробляйте помилки
try:
r = session.get(url, timeout=(5, 20))
r.raise_for_status()
except requests.exceptions.ProxyError:
print("The proxy refused, dropped or did not answer the connection")
except requests.exceptions.Timeout:
print("Timed out: raise the timeout or lower the concurrency")
except requests.exceptions.RequestException as exc:
print("Request failed:", type(exc).__name__)Виводьте клас винятку, а не все повідомлення: у повідомленнях можуть бути імена хостів і URL, яким не місце у ваших журналах.
7.Перевірка IP та його зміна
Зі стабільним IP ваші запити виходять з тієї самої адреси, поки ви продовжуєте працювати. Натисніть «Змінити IP» у кабінеті, коли потрібна нова; керування зміною IP через API заплановане. Ця функція читає поточний вихідний IP, тож ви можете переконатися, що зміна відбулася:
import requests
def new_session():
s = requests.Session()
s.proxies.update(proxies)
return s
def exit_ip(session):
r = session.get("https://xameleon.cloud/api/tools/ip", timeout=(5, 20))
r.raise_for_status()
return r.json()["ip"]
session = new_session()
before = exit_ip(session)
input("Press Change IP in your account, then press Enter... ")
session.close() # drop connections that still point at the old IP
session = new_session()
after = exit_ip(session)
print("IP changed" if after != before else "Same IP: check that the change went through")Змінюйте IP між завданнями, а не посеред входу в акаунт чи оформлення замовлення. У матеріалі про ротацію проксі показано шаблони, що поєднують зміну на вимогу з кількома проксі.
8.Проксі з httpx та асинхронний код
httpx працює за тією ж ідеєю з дещо іншим API та підтримкою асинхронності. Передайте проксі клієнтові. Версії httpx старіші за 0.26 приймають proxies= замість proxy=.
import httpx
proxy = "http://USERNAME:PASSWORD@HOST:PORT"
timeout = httpx.Timeout(20.0, connect=5.0)
with httpx.Client(proxy=proxy, timeout=timeout) as client:
r = client.get("https://example.com")
print(r.status_code)Для SOCKS5 встановіть додатковий пакет командою python -m pip install "httpx[socks]" і використовуйте URL із socks5://. Для багатьох одночасних запитів скористайтеся AsyncClient із семафором, щоб кількість одночасних запитів не перевищувала ліміт вашого тарифу:
import asyncio
import httpx
URLS = [f"https://example.com/page/{i}" for i in range(1, 11)]
async def main():
sem = asyncio.Semaphore(5) # at most 5 requests at a time
async with httpx.AsyncClient(proxy=proxy, timeout=20) as client:
async def fetch(url):
async with sem:
r = await client.get(url)
return url, r.status_code
for url, status in await asyncio.gather(*(fetch(u) for u in URLS)):
print(url, status)
asyncio.run(main())9.Паралельні запити в межах лімітів
Session не задокументована як потокобезпечна, тому дайте кожному робочому потоку власну сесію. Тримайте max_workers не вище за кількість одночасних з’єднань, яку дозволяє ваш тариф.
import threading
from concurrent.futures import ThreadPoolExecutor
local = threading.local()
def get_session():
if not hasattr(local, "session"):
local.session = requests.Session()
local.session.proxies.update(proxies)
return local.session
def fetch(url):
r = get_session().get(url, timeout=(5, 20))
return url, r.status_code
with ThreadPoolExecutor(max_workers=5) as pool:
for url, status in pool.map(fetch, urls):
print(url, status)Паралельні запити використовують той самий IP і той самий обсяг трафіку. Більше потоків не змушує сайт приймати більше запитів, тож зменшуйте їхню кількість, коли бачите відповіді 429.
10.Типові помилки та як їх виправити
Повідомлення підказує, де шукати причину. Ось помилки, які найчастіше трапляються з requests:
| Що ви бачите | Імовірна причина | Виправлення |
|---|---|---|
ProxyError з Tunnel connection failed: 407 Proxy Authentication Required | Неправильний логін або пароль | Введіть облікові дані з кабінету ще раз і закодуйте їх. Для звичайних сторінок http:// ви отримаєте відповідь зі статусом 407, а не виняток. |
ProxyError з Failed to establish a new connection | Неправильний хост чи порт або не той протокол | Перевірте хост, порт і протокол (HTTP чи SOCKS5) точно так, як у кабінеті. |
ProxyError з ConnectTimeoutError | Проксі недоступний із вашої мережі | Перевірте хост, брандмауер і мережу. З проксі тайм-аут підключення позначається як ProxyError, а не ConnectTimeout. |
ReadTimeout | Сайт або мобільне з’єднання відповіли надто повільно | Збільште тайм-аут читання, зменште паралельність і повторіть запит із наростаючою затримкою. |
InvalidURL: Failed to parse | Спеціальні символи в URL проксі | Закодуйте логін і пароль відсотковим кодуванням за допомогою quote(value, safe=""). |
InvalidSchema: Missing dependencies for SOCKS support | Не встановлено додаткового пакета SOCKS | Виконайте python -m pip install "requests[socks]". |
SSLError з CERTIFICATE_VERIFY_FAILED | Застарілий або корпоративний набір сертифікатів на вашому боці | Оновіть certifi або вкажіть правильний набір у REQUESTS_CA_BUNDLE. Не вимикайте перевірку через verify=False. |
| Сайт показує вашу власну IP-адресу | Проксі не використовується: задано лише один із ключів http і https | Задайте обидва ключі в proxies і перевірте результат викликом ендпоінта IP. |
11.Поводьтеся коректно
Додавайте затримки між запитами, дотримуйтесь robots.txt та умов кожного сайту й тримайте паралельність у межах лімітів вашого тарифу.
Щоб перевірити robots.txt через той самий проксі, завантажте його своєю сесією й передайте парсеру зі стандартної бібліотеки:
import random
import time
from urllib.robotparser import RobotFileParser
base = "https://example.com"
r = session.get(base + "/robots.txt", timeout=(5, 20))
robots = RobotFileParser()
robots.parse(r.text.splitlines() if r.ok else []) # no robots.txt: nothing is disallowed
for path in ["/page/1", "/page/2"]:
if not robots.can_fetch("my-app", base + path):
continue # the site asks bots to stay out
r = session.get(base + path, timeout=(5, 20))
time.sleep(random.uniform(1, 3)) # a pause between requestsПозначайте свою програму зрозумілим User-Agent, збирайте лише ті дані, на які маєте право, і читайте умови сайту, з яким працюєте. Бюджет запитів і обробку помилок докладніше розглянуто в матеріалі про веб-скрейпінг у великому масштабі.
12.Часті запитання
Яка бібліотека Python найкраща для роботи з проксі?
requests — найпростіший вибір для скриптів. httpx додає підтримку асинхронності та суворішу модель тайм-аутів. Обидві приймають однаковий URL проксі, тож перейти з однієї на іншу можна, не змінюючи дані проксі.
Чи використовує Python системний проксі автоматично?
requests і httpx за замовчуванням читають змінні середовища HTTP_PROXY, HTTPS_PROXY і NO_PROXY. Щоб їх ігнорувати, задайте trust_env=False.
Що обрати: HTTP чи SOCKS5?
Для вебзапитів використовуйте HTTP(S). SOCKS5 обирайте, коли цього потребує інструмент, і віддавайте перевагу схемі socks5h://.
Як зберегти той самий IP для всіх запитів?
Ваш IP стабільний: він лишається тим самим, поки ви продовжуєте працювати, а коли потрібен новий, ви змінюєте його в кабінеті. Використовуйте одну сесію на завдання й не натискайте «Змінити IP» посеред нього.
Як використовувати кілька проксі в Python?
Створіть окрему сесію для кожного проксі та вибирайте між ними у своєму коді. Шаблони показано в матеріалі про ротацію проксі.
Чи можна використовувати проксі з Playwright?
Так. Передайте проксі під час запуску браузера: proxy={"server": "http://HOST:PORT", "username": "USERNAME", "password": "PASSWORD"}.
Лишилися запитання про мобільні проксі? Відвідайте нашу сторінку FAQ щоб отримати детальні відповіді, або зверніться до нашої підтримки — ми допоможемо.