Если у вас простой скрипт и небольшое количество страниц к обходу, то парсер может работать в полуручном режиме, это когда вы будете находиться рядом и в любой момент будете готовы решить капчу.
Но чем больше становятся объёмы парсинга, тем больше времени придётся тратить на обход систем защиты от ботов. Более простое и эффективное решение — работа через прокси-серверы, желательно с автоматической ротацией.
Если вы пишите свой скрипт на Python и в нём задействуете библиотеку Requests, этот материал для вас. Расскажем как использовать прокси с Python Requests: как, что и где настраивать, нужно ли ротировать прокси, с какими подводными камнями или ошибками можно столкнуться, как их обойти.
Что такое Python Requests?
Python Requests — это библиотека, которая отвечает за обработку (отправку и приём) HTTP-запросов: технических заголовков, ошибок и содержимого.
Вот официальный сайт Python Requests + там же подробная техническая документация.
Как утверждают разработчики библиотеки, Python Requests — это HTTP-запросы «для людей».
В норме Python Requests работает в связке с ещё одной библиотекой — urllib3. Последняя в свою очередь отвечает за создание HTTP-клиента на языке программирования Python, реализует обработку и проверку SSL/TLS-сертификатов (тех, что нужны для шифрования трафика и организации защищённого HTTPS-соединения), сжимает и распаковывает контент, передаваемый в формате gzip, deflate, brotli и zstd.
Python Requests «очеловечивает» этот функционал за счёт понятного и простого синтаксиса.
Ключевые возможности библиотеки:
- Удержание подключений и множественные соединения.
- Ввод любых доменов и URL-адресов.
- Обработка кук и сессий, SSL-сертификатов (по аналогии с тем, как это делают реальные браузеры), кодов ответа сервера, заголовков, форм, файлов и т.п.
- Полная поддержка GET, POST, PUT, DELETE, HEAD, OPTIONS и других типов запросов.
- Встроенная поддержка работы с JSON.
- Возможность обработки исключений и ошибок.
- Автоматическое перекодирование тела ответов в Unicode.
- Работа с таймаутами соединений.
- Возможность подключения через прокси.
- Все самые востребованные типы аутентификации: базовая, дайджест, OAuth (версии 1 и 2), OpenID Connect и т.п.
- Подробная документация и API.
- Простой и «человечный» синтаксис, особенно, если сравнивать с такими библиотеками, как urllib2.
Python Requests — это одна из самых популярных библиотек для написания своих парсеров.
Понимание работы прокси в Python Requests
Прокси — это промежуточный сервер (компьютер или любое другое устройство в сети), который выступает посредником между пользователем и целевым ресурсом (например, веб-сайтом или веб-приложением).
Когда запрос оправляется через прокси, то этот сервер перенаправляет его на целевой ресурс от своего имени, а ваши данные обычно скрывает. Соответственно, целевой ресурс обслуживает прокси и не знает о вашем существовании.
Прокси часто используют для повышения конфиденциальности, обхода региональных ограничений, ускорения работы сети через системы кэширования (прокси может дополнительно видоизменять ваши запросы и контент), а также для контроля доступа и распределения ресурсов/трафика в корпоративных сетях.
Прокси бывают разных типов. Например, прокси могут перенаправлять ваши запросы «как есть» (они будут называться «транспарентные» или «прозрачные»), указывать специальные заголовки, но скрывать ваши данные (частично анонимные) и полностью убирать все следы своей работы (это анонимные или элитные прокси).
- По типу подключения они обычно разделяются на основании поддерживаемых протоколов: HTTP-прокси, SOCKS-прокси (как можно догадаться, используют протокол SOCKS), SSH-прокси и т.д. Подробнее в сравнении HTTP vs SOCKS-прокси.
- По типу выходных адресов они могут быть резидентными (работают на базе IP-адресов домашних пользователей/устройств), мобильными (IP принадлежат пулам, за которые отвечают операторы сотовой связи) и серверными. Реже выделяют корпоративные прокси, они работают на базе IP, принадлежащих компаниям и юридическим лицам.
- По типу ротации прокси могут быть статическими (когда каждый отдельный прокси закрепляется за клиентом и работает с постоянным IP-адресом) или ротируемыми (они же backconnect-прокси, прокси с обратной связью, подразумевают статическую точку входа в прокси-сеть и автоматическую ротацию выходных IP-адресов).
Как можно догадаться, прокси разных типов могут комбинироваться между собой. Например, резидентные прокси могут работать по схеме с обратной связью на HTTP-протоколе и быть максимально анонимными.
Осталось разобраться с добавлением прокси к Python Requests.
Библиотека Requests работает с HTTP/HTTPS-протоколом, поэтому в паре с ней логичнее всего использовать HTTP/HTTPS-прокси.
Но если вам принципиально наличие поддержки SOCKS-прокси, то нужно будет дополнительно установить расширение requests[socks].
Прокси в Python Requests могут использоваться на уровне отдельных запросов или для целых сессий (когда ваш скрипт подключается к сайту и обменивается с ним серией запросов/ответов).
Но хватит теории, давайте лучше перейдём к практике.
Базовая настройка прокси в Python Requests
Предполагается, что у вас уже установлено и настроено окружение для языка Python. Если этого ещё не сделано, то скачайте дистрибутив с официального сайта и установите его в своей операционной системе. В Linux-дистрибутивах для этого можно воспользоваться штатным менеджером пакетов (APT, Pacman, DPKG, RPM) и дефолтными репозиториями. Где-то Python уже даже предустановлен в системе.
В Windows-системах позаботьтесь о том, чтобы Python добавился в переменные среды (Path). По аналогии нужно добавить вызов апплета pip (это менеджер пакетов для Python).
Пример того, как это может выглядеть:
Осталось установить саму библиотеку. Это делается командой в консоли:
pip install requests
Если с добавлением апплета pip вы не разобрались, то можно воспользоваться такой командой:
python -m pip install requests
Для добавления библиотеки в свои скрипты её нужно импортировать. Делается это в коде:
# Ваш первый Python-скрипт, все строки, начинающиеся с решётки — это комментарии, они не участвуют в логике вашей программы…
import requests
# Ниже остальной код скрипта
…
Python Requests с использованием прокси
Самая элементарная интеграция прокси в ваш парсер:
# Сначала импортируем библиотеку requests
import requests
# Далее определяем параметры своих прокси-серверов, для этого создаём словарь (набор переменных)
my_proxies = {
# В словаре создаём переменные и задаём их значения
'http': 'http://123.58.199.17:8168',
'https': 'http://123.58.199.17:8168',
}
# При отправке запроса добавляем к нему свои прокси.
# В конструкции ниже используется метод GET, но его можно заменить на POST и любые другие, в зависимости от задач скрипта.
response = requests.get('http://google.com', proxies=my_proxies)
# А тут выводим в консоль код ответа сервера.
# Код, который мы ждём для нормальной работы, 200
print(f"The server returned code: {response.status_code}")
# Ниже остальной код Python-парсера…
Давайте немного усложним скрипт: добавим заголовок с юзер-агентом (отвечает за то, как представляется браузер) и вывод кодов ответа сервера:
# Сначала импортируем библиотеку requests
import requests
# Затем определяем список прокси
proxies = {
'http': 'http://198.49.68.80:80', # Пример HTTP прокси
'https': 'https://198.49.68.80:80' # Пример HTTPS прокси
}
# Тут заголовки с User-Agent
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
}
# Отправка запроса через прокси
try:
response = requests.get('http://www.google.com', proxies=proxies, headers=headers, timeout=5)
# В консоль возвращается ответ сервера.
# Нам нужен код 200, это значит, что всё ОК
print(f"Server response code: {response.status_code}")
# Ошибки обрабатываем отдельно и тоже выводим в консоль, так проще заниматься отладкой))
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
Как настроить прокси в Python Requests на уровне сессии
Библиотека Requests поддерживает работу через прокси на уровне сессий.
Код Python-парсера будет выглядеть следующим образом:
# Сначала импортируем библиотеку requests
import requests
# Далее определяем параметры своих прокси-серверов, для этого создаём словарь (набор переменных)
proxies = {
'http': 'http://45.155.226.176:3128',
'https': 'http://172.67.182.20:1080',
}
# Добавляем метод обработки сессий
session = requests.Session()
# Добавляем к сессии параметры прокси
session.proxies.update(proxies)
# Теперь через механизм сессий обращаемся к нужному сайту
# «example-site.com» нужно заменить на свой адрес
session.get('http://example-site.com')
# Дальше остальная логика вашего Python-скрипта…
Обратите внимание! Ещё в 2014 году разработчики выяснили, что метод session.proxies ведёт себя не так, как того хотелось бы. Дело в том, что он завязан на переменные, которые возвращает конструкция urllib.request.getproxies (то есть перезаписывает переменные на уровне среды). В связи с этим рекомендуется изначально определять прокси на уровне запросов — с помощью конструкции, которую мы упомянули самой первой. Проблема с сессиями до сих пор остаётся актуальной.
Использование прокси с аутентификацией
Прокси необязательно должны указываться в виде IP-адреса. Вы вполне можете использовать строковые значения с доменами. Например, вместо конструкции вида:
'http': 'http://45.155.226.176:3128',
Можно задать что-то типа:
'http': 'http://my-proxies-for-python-requests.com:3128',
Даже если ваш прокси-сервер работает с обязательной авторизацией по логину и паролю, это не проблема. Достаточно правильно вписать параметры. Конструкция должна выглядеть так:
http://[YOUR_USERNAME]:[PASSWORD]@[HOST]:[PORT_NUMBER]
Более предметный пример:
'http': 'http://my-real-login:1234PASS4321@45.155.226.176:3128',
Или:
'http': 'http://my-real-login:1234PASS4321@my-proxies-for-python-requests.com:3128',
Прокси для Python Requests на уровне переменных среды
Так как параметры прокси можно отнести к чувствительной (почти конфиденциальной) информации, то их логично хранить отдельно от основного кода.
Библиотека Python Requests поддерживает обращения к переменным среды.
Вы можете определить свои прокси так (команды для консоли):
export HTTP_PROXY="http://45.155.226.176:80"
export HTTPS_PROXY="http://45.155.226.176:8080"
Если вы работаете в среде Windows, то вместо команды «export» используйте «set».
После установки переменных среды, упоминание прокси можно полностью удалить из своих скриптов. Они будут подставляться автоматом.
Как и в случае с локальными параметрами запросов, прокси на уровне переменных среды поддерживают конструкцию вида: [YOUR_USERNAME]:[PASSWORD]@[HOST]:[PORT_NUMBER]
Использование прокси SOCKS с Python Requests
Начиная с версии Python Requests 2.10 библиотека поддерживает работу через SOCKS-прокси. Но для этого нужно отдельно доустановить расширение:
pip install requests[socks]
Socks-прокси в ваших парсерах и скриптах будут прописываться по аналогии с HTTP-прокси:
proxies = {
'http': 'socks5://your-user:pass@proxy-host:port',
'https': 'socks5://your-user:pass@proxy-host:port'
}
Собственно, никаких отличий нет, кроме указания протокола SOCKS5.
Если вы хотите подключить Socks5-прокси для Python Requests на уровне переменных среды, то используйте ALL_PROXY. Пример:
export ALL_PROXY="socks5://45.155.226.176:3434"
Для Windows:
set ALL_PROXY="socks5://45.155.226.176:3434"
Обработка ошибок прокси и устранение неполадок
Все классы для обработки ошибок и исключений для библиотеки Requests можно найти в файле:
Python313\Lib\site-packages\requests\exceptions.py
Python313 — это общий каталог, в который был установлен Python (обычно сопровождается номером текущей актуальной версии, в нашем случае 3.13).
В частности, в этом файле определена обработка ошибок для:
- таймаутов,
- URL-запросов (если конструкция будет неправильной),
- редиректов,
- неправильных схем и заголовков,
- подключений (в том числе отдельно для коннектов с использованием SSL),
- кодировки и прочего.
Пример вывода ответов сервера:
# Тут выводим ответ сервера
print(f"Server response code: {response.status_code}")
# Тут выводим ошибки и исключения библиотеки
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
Ещё пример, в котором мы переходим к парсингу, если код ответа сервера равен 200 (то есть запрашиваемая страница доступна):
# Проверяем ответ сервера на наличие кода 200
if response.status_code == 200:
# Если код соответствует, печатаем в консоль, что всё ОК
print("Server OK, there are no errors")
# А дальше парсим содержимое с помощью BeautifulSoup
soup = BeautifulSoup(response.content, "html.parser")
# Возвращаем содержимое страницы (то есть HTML-код)
return soup
# В остальных случаях выводим только код ошибки
else:
print(f"The server returned an error: {response.status_code}")
# И ничего не возвращаем
return None
Раз упомянули — материал по работе с BeautifulSoup.
Резидентные прокси
Лучшие прокси-серверы для доступа к ценным данным со всего мира.
Ротация прокси для веб-скрапинга с Python Requests
В нашем примере ниже нужно добавить все ваши прокси списком. Скрипт посчитает количество элементов в нём и выберет случайную строку. Через выбранный прокси и будет отправлен результирующий запрос.
При новом запуске скрипта будет выбран новый случайный прокси.
Сам скрипт:
# Импортируем библиотеку Python Requests и Random (для работы со случайными числами)
import requests
import random
# Здесь вписываем данные для подключения к вашим прокси, количество элементов может быть любым
proxy_list = [
"http://222.252.194.204:5000",
"http://my-proxy-service.com:5001",
"http://162.223.90.130:5100",
"http://my-real-login:1234PASS4321@45.155.226.176:3128",
"http://50.217.226.41:5050",
"http://211.128.96.206:8080",
"http://72.10.160.170:8001"
]
# Тут Описываем логику функции, которая будет отвечать за отправку запросов через прокси с ротацией
# В качестве параметров на входе подаём тип HTTP-запроса, целевой URL-адрес и "волшебные" аргументы
def proxy_request(request_type, url, **kwargs):
while True:
try:
# Устанавливаем текущий прокси
# Для этого определяем случайное целое число от нуля до количества элементов в списке прокси, уменьшенного на один (так как нумерация элементов списков начинается с нуля, то она заканчивается на N-1)
# Назначаем элемент с этим номером в качестве текущего прокси
current_proxy = random.randint(0, len(proxy_list) - 1)
# Устанавливаем параметр proxies по стандартной схеме, выставляем его и для HTTP, и для HTTPS-протокола
proxies = {"http": proxy_list[current_proxy], "https": proxy_list[current_proxy]}
# Отправляем запрос к странице с выбранным прокси
response = requests.request(request_type, url, proxies=proxies, timeout=5, **kwargs)
# Выводим в консоль, какой конкретно прокси сейчас используется
print(f"Proxy used: {current_proxy['https']}")
# Прерываем цикл
break
# Обрабатываем исключения и ошибки
except Exception as err:
# Если прокси не заработал, пишем в консоли
print("Error, restart the script and proxy will change")
# Возвращаем результирующий запрос
return response
# Запускаем нашу функцию и передаём ей нужные параметры, как минимум - тип запроса "GET" и целевой URL-адрес
proxy_request('GET', 'http://site.com')
Вместо выбора случайного номера можно организовать последовательный перебор — от первого до последнего элемента списка. Тогда при каждом новом запросе нужно наращивать стартовый счётчик на единицу. При полном исчерпании элементов перебор можно перезапустить с нуля.
Более простой и логичный способ — использование ротируемых прокси. Например, на основе сервиса Froxy.
В этом случае ротация прокси для Python Requests будет выполняться на стороне сервиса. Логика настраивается в личном кабинете (возможны разные подходы: по времени, при каждом новом запросе, с максимальным удержанием IP и т.п.), там же настраивается география расположения прокси-сервера.
Для библиотеки Python Requests (и для всего парсера) подключение прокси будет выполняться всего один раз. Вам достаточно указать параметры подключения к порту — он выглядит как обычный прокси и подключается также, просто он не является конечной точкой выхода (это точка входа в прокси-сеть). Подробности в материале про BackConnect-прокси.
Лучшие практики и соображения безопасности
Современные сайты активно борются с паразитным трафиком и используют разные механизмы защиты.
Да, отправка запросов из библиотеки Python Requests через прокси — это одно из самых эффективных решений для обхода защиты и для повышения своей анонимности (конфиденциальности).
Но только прокси может быть недостаточно. Вот ряд советов для качественного парсинга, «лучшие практики»:
- Используйте случайные задержки между запросами. Равные промежутки между запросами однозначно выдают ботов и парсеры.
- Не отправляйте запросы к целевому сайту слишком часто. Во-первых, это приводит к повышению нагрузки на него, а во-вторых, вас проще вычислить и заблокировать. Большое количество запросов логично отправить только через разные прокси (для распараллеливания процесса парсинга и для обхода систем защиты).
- Если есть сомнения в качестве прокси, организуйте их предварительное тестирование.
- Следите за правильным указанием user-agent и других заголовков.
- Качественно эмулируйте набор кук и других параметров цифровых отпечатков. Если пользовательских профилей становится слишком много, включайте в работу антидетект-браузеры.
- Если на сайте присутствуют динамические элементы или он полностью написан на JavaScript, библиотека Python Requests не поможет с обработкой результирующего HTML-кода. Вместо неё нужно использовать headless-браузер.
- Следите за наличием ловушек в коде (Honeypot).
- Задумайтесь об интеграции сервисов для автоматического разгадывания капчи (это самый популярный способ защиты от ботов — первая линия обороны).
- Если у сайта или веб-сервиса есть официальный API интерфейс, лучше обращайтесь к нему. К слову, библиотека Python Requests из коробки поддерживает JSON-формат.
Больше деталей в статье про лучшие практики для веб-скрапинга без блокировок
Заключение и рекомендации
Библиотека Requests — это мощный инструмент, который определённо может быть полезен при написании собственных парсеров на языке Python. Она обеспечивает «очеловеченный» синтаксис и имеет массу встроенных методов и функций для работы с HTTP/HTTPS-запросами.
Слабое звено любого парсера — возможность его обнаружения и блокирования. Чтобы избежать блокировок, как минимум нужно использовать прокси-серверы. Несмотря на то, что прокси подключаются к Python Requests парой строк кода, немаловажное значение имеет качество и тип прокси. Лучшим решением будут анонимные ротируемые прокси на базе мобильных или резидентных IP-адресов.
Найти такие прокси можно у нас. Для тестирования Froxy можно воспользоваться недорогим trial-пакетом.
Пул наших IP-адресов насчитывает свыше 10 млн. Ротация возможна по времени или при каждом новом запросе. Таргетинг — до города и оператора связи