Прокси в Python requests: настройка, авторизация и ротация

Чтобы отправить запрос requests через прокси, достаточно передать словарь proxies, где ключ — схема сайта, а значение — адрес прокси: например, {"https": "http://user:pass@host:port"}. Его можно указать в отдельном вызове или один раз в объекте Session. Всё остальное — авторизация, SOCKS5, переменные окружения, смена IP — надстройки над этим словарём.
На базовом примере большинство инструкций и заканчивается, хотя реальные скрипты ломаются на мелочах: пароль с символом @, DNS-запросы мимо туннеля, системная переменная HTTPS_PROXY, которая незаметно перебивает настройки в коде, или пул адресов, где один IP внезапно перестал отвечать. Ниже — рабочие фрагменты кода для каждого случая, таблица типичных исключений и подсказка, какой тип прокси взять.
Ротационные прокси для Python
Резидентские и ротационные прокси с логином и паролем, которые сразу работают в requests
Что понадобится
- Python 3.9 или новее и сама библиотека:
pip install requests. - Данные прокси от провайдера: хост, порт, протокол (HTTP или SOCKS5), при необходимости логин и пароль.
- Сервис, который показывает ваш IP. В примерах —
https://api.ipify.orgиhttps://httpbin.org/ip, подойдёт любой аналог.
Адрес 203.0.113.15 и логин user-7f3k в примерах условные. Подставьте свои значения и не храните настоящие пароли в коде — как этого избежать, описано в разделе о переменных окружения.
Как устроен словарь proxies
requests выбирает прокси по схеме целевого адреса, а не самого прокси. Ключ https срабатывает для всех страниц https://, ключ http — для страниц http://. Значение — адрес прокси-сервера, и его схема определяет, как библиотека общается именно с прокси.

import requests
shluz = "http://203.0.113.15:8000"
marshrut = {"http": shluz, "https": shluz}
otvet = requests.get("https://api.ipify.org?format=json", proxies=marshrut, timeout=15)
print(otvet.json())Оба ключа ведут на прокси со схемой http://, и это нормально для большинства провайдеров. Клиент подключается к прокси обычным соединением и просит открыть CONNECT-туннель, а TLS-сессия с сайтом идёт внутри туннеля, так что трафик остаётся зашифрованным. Схема https:// перед адресом прокси нужна, только если провайдер прямо поддерживает TLS на порту прокси. Подробнее разница разобрана в статье про HTTP- и HTTPS-прокси.
Можно задавать прокси и для отдельных хостов: ключ вида "https://example.org" перекрывает общий https только для этого домена. Это удобно, когда одному сайту нужна другая страна выхода.
Авторизация по логину и паролю
Платные прокси почти всегда требуют логин и пароль. requests берёт их прямо из адреса прокси:
from urllib.parse import quote
login = "user-7f3k"
parol = quote("p@ss:word/42", safe="") # кодируем @ : / и похожие символы
adres = f"http://{login}:{parol}@203.0.113.15:8000"
otvet = requests.get("https://httpbin.org/ip",
proxies={"http": adres, "https": adres},
timeout=15)Кодирование здесь не формальность. Если в пароле есть @, :, / или #, разбор адреса обрежет его не там, и прокси ответит кодом 407 Proxy Authentication Required. Функция quote(..., safe="") превращает такие символы в последовательности вида %40.
Второй вариант — авторизация по IP: вы добавляете внешний адрес своего сервера в белый список в панели провайдера и подключаетесь без логина. Для постоянного сервера это удобно, а вот на ноутбуке или домашнем интернете, где внешний IP меняется, доступ будет периодически пропадать.
SOCKS5-прокси в requests
Поддержка SOCKS — необязательное дополнение, которое ставит библиотеку PySocks:
pip install "requests[socks]"Дальше тот же словарь, только со SOCKS-схемой:
tunnel = "socks5h://user-7f3k:s3cret@203.0.113.15:1080"
otvet = requests.get("https://api.ipify.org", proxies={"http": tunnel, "https": tunnel}, timeout=20)Обратите внимание на букву h. Со схемой socks5:// доменное имя разрешает ваш компьютер и передаёт прокси готовый IP: DNS-запросы уходят через вашу сеть, а CDN может выбрать сервер под ваш реальный регион. Со схемой socks5h:// имя уходит на прокси и разрешается там. Для парсинга и проверок по странам почти всегда нужен socks5h. Когда вообще выбирать SOCKS5, а когда HTTP, рассказано в сравнении SOCKS5 и HTTP-прокси.
Ошибка InvalidSchema: Missing dependencies for SOCKS support означает, что дополнение не установлено в активное виртуальное окружение.
Сессии: один прокси на серию запросов
Session хранит cookies и переиспользует TCP-соединения, так что не тратит время на новое рукопожатие при каждом запросе. Прокси, заданные у сессии, действуют на все её вызовы:
sbornik = requests.Session()
sbornik.proxies.update({"http": adres, "https": adres})
sbornik.headers["User-Agent"] = "price-monitor/1.4 (+contact@example.com)"
for stranica in range(1, 4):
kartochki = sbornik.get(f"https://example.org/catalog?page={stranica}", timeout=15)
print(stranica, kartochki.status_code)Есть нюанс, о котором мало кто знает: при включённом trust_env (по умолчанию так и есть) прокси из переменных окружения могут оказаться важнее, чем session.proxies. Если нужно, чтобы всегда работали настройки сессии, передавайте proxies= в каждый вызов или отключите окружение: sbornik.trust_env = False.
Прокси через переменные окружения
requests понимает стандартные переменные HTTP_PROXY, HTTPS_PROXY, ALL_PROXY и NO_PROXY (в нижнем регистре тоже). Так адрес и пароль не попадают в код и репозиторий:
# Linux / macOS
export HTTPS_PROXY="http://user-7f3k:s3cret@203.0.113.15:8000"
export NO_PROXY="localhost,127.0.0.1,.internal.lan"
# Windows PowerShell
$env:HTTPS_PROXY = "http://user-7f3k:s3cret@203.0.113.15:8000"После этого requests.get("https://api.ipify.org") пойдёт через прокси без дополнительных аргументов. В NO_PROXY перечисляют адреса, к которым нужно ходить напрямую, например локальные сервисы. Если хочется явного контроля, читайте значение сами через os.environ.get("SCRAPER_PROXY") и собирайте словарь из него.
Ротация прокси в Python requests
Менять IP между запросами можно двумя способами.

Способ 1. Перебор собственного списка
Если провайдер выдал список статических адресов, перебирайте их по кругу и пропускайте те, что не отвечают:
import itertools
import requests
spisok = [
"http://user-7f3k:s3cret@203.0.113.15:8000",
"http://user-7f3k:s3cret@203.0.113.27:8000",
"http://user-7f3k:s3cret@203.0.113.41:8000",
]
krug = itertools.cycle(spisok)
def zagruzit(url, popytki=3):
for _ in range(popytki):
vyhod = next(krug)
try:
resp = requests.get(url, proxies={"http": vyhod, "https": vyhod}, timeout=12)
if resp.status_code in (403, 429):
continue # этот IP упёрся в лимит, берём следующий
return resp
except (requests.exceptions.ProxyError, requests.exceptions.ConnectTimeout):
continue
raise RuntimeError(f"не удалось загрузить {url}")itertools.cycle даёт равномерный перебор по кругу. random.choice(spisok) тоже работает, но может выбрать один и тот же адрес несколько раз подряд.
Способ 2. Ротирующий шлюз (backconnect)
С ротационными прокси вы подключаетесь к одному хосту и порту, а провайдер сам меняет внешний IP — на каждый запрос или по таймеру. В коде остаётся обычная настройка одного прокси, а ротация, проверка пула и выбор страны происходят на стороне шлюза. Часто можно закрепить один IP на несколько минут (sticky-сессия) параметром в логине. Чем отличаются эти режимы, объясняет статья о sticky- и ротационных прокси.

Как проверить, что прокси работает
Сравните адрес без прокси и через него:
napryamuyu = requests.get("https://api.ipify.org", timeout=10).text
cherez_proxy = requests.get("https://api.ipify.org", proxies=marshrut, timeout=10).text
print("напрямую:", napryamuyu, "| через прокси:", cherez_proxy)Если адреса совпали, прокси не используется. Чаще всего виновата опечатка в ключах ("HTTPS" вместо "https") или переменная окружения, которая перехватила настройки. Для проверки страны откройте любой сервис геолокации IP и сравните результат с заказанной локацией.
Частые ошибки и что с ними делать
| Симптом | Вероятная причина | Что сделать |
|---|---|---|
ProxyError: Tunnel connection failed: 407 | Неверный логин или пароль, спецсимволы без кодирования | Проверить данные, обернуть пароль в quote() |
ProxyError: Cannot connect to proxy | Неверный хост или порт, прокси недоступен, файрвол | Проверить тот же адрес через curl, сверить порт |
ConnectTimeout / ReadTimeout | Медленный или перегруженный узел | Задать timeout=(5, 20), повторить с другим IP |
SSLError при адресе прокси с https | Схема https:// у прокси, который работает по HTTP | Указать http:// в адресе прокси |
InvalidSchema для socks | Не установлен PySocks | pip install "requests[socks]" |
| Сайт отвечает 403 или 429 | Лимит запросов или репутация IP | Снизить частоту, менять IP, добавить паузы |
Всегда задавайте timeout. Без него requests может бесконечно ждать ответа от мёртвого прокси и подвесить весь обработчик. Для повторов с нарастающей паузой подключите к сессии HTTPAdapter с urllib3.util.Retry. Более широкий список того, что ломает сбор данных, собран в статье об ошибках при парсинге сайтов.
Какой тип прокси выбрать для requests
- Серверные (датацентровые) — быстрые и недорогие; подходят для API, внутренних инструментов и сайтов без жёсткой защиты от ботов.
- Резидентские — IP домашних подключений, легче проходят проверки репутации; основной выбор для мониторинга цен и сбора открытых данных.
- Мобильные — IP операторов связи, общие с реальными пользователями телефонов; нужны для мобильных версий и самых требовательных сайтов.
Какой бы тип вы ни выбрали, держите нагрузку в разумных пределах: соблюдайте robots.txt и правила сайта, кэшируйте уже скачанное и пользуйтесь официальным API, если он есть. Обзор схем и типовых конфигураций — в материале о прокси для парсинга.
Частые вопросы о прокси в Python requests
Как подключить прокси в Python requests?
Создайте словарь с ключами http и https, где значением будет адрес прокси, и передайте его аргументом proxies в requests.get() или запишите в session.proxies. Ключ — схема сайта, который вы открываете, значение — адрес прокси-сервера.
Как передать логин и пароль прокси в requests?
Укажите их в адресе прокси в формате http://login:password@host:port. Если в пароле есть спецсимволы, сначала закодируйте его через urllib.parse.quote, иначе прокси отклонит авторизацию с ошибкой 407.
Поддерживает ли requests SOCKS5?
Да, после установки дополнения командой pip install requests[socks]. Используйте схему socks5h, чтобы доменные имена разрешал прокси и DNS-запросы не утекали.
Почему requests не использует мой прокси?
Обычно причина в ключах словаря с заглавными буквами, в переменной окружения вроде HTTPS_PROXY, которая перекрывает настройки сессии, или в хосте из списка NO_PROXY. Передайте proxies= прямо в вызов или установите у сессии trust_env = False, чтобы исключить влияние окружения.
Как сделать ротацию прокси в requests?
Либо перебирайте список адресов через itertools.cycle с повтором при ошибках, либо подключитесь к ротирующему шлюзу, который сам выдаёт новый IP. Во втором случае логика ротации в коде не нужна.
Адрес прокси начинать с http или https?
Почти всегда с http://, даже если вы открываете HTTPS-сайты: трафик до сайта остаётся зашифрованным внутри CONNECT-туннеля. Схему https:// ставьте, только если провайдер прямо указывает TLS на порту прокси.


