Попробовать
Назад к блогу

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

Прокси в 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 между запросами можно двумя способами.

Ротация IP в скрипте
Ротация 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- и ротационных прокси.

Python-скрипт отправляет три запроса на один ротирующий шлюз, который пропускает каждый через другой резидентный IP к одному сайту
С ротирующим шлюзом скрипт всегда использует один адрес прокси, а каждый запрос приходит на сайт с другого резидентного IP

Как проверить, что прокси работает

Сравните адрес без прокси и через него:

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Не установлен PySockspip 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 на порту прокси.