HTTP-библиотеки Python: сравнение urllib, urllib2, urllib3, requests
Для кого эта статья:
- Начинающие и опытные разработчики Python, интересующиеся веб-разработкой
- Программисты, работающие с HTTP-запросами и REST API
Люди, изучающие выбор библиотек для оптимизации выполнения кода и повышения его эффективности
Выбор HTTP-библиотеки для Python — это как выбор автомобиля: всё зависит от того, куда вы едете и с какой скоростью. Старичок urllib, его последователи urllib2 и urllib3, или современный фаворит requests — каждый инструмент имеет свои сильные и слабые стороны. Разбираться в нюансах каждой библиотеки критично, если вы хотите, чтобы ваш код не просто работал, а летал 🚀. Четыре конкурента, четыре подхода к HTTP-запросам — давайте разберемся, кто из них заслуживает место в вашем арсенале.
Желаете углубиться в мир веб-разработки на Python и научиться профессионально работать с HTTP-библиотеками? Обучение Python-разработке от Skypro даст вам не только теоретические знания, но и практические навыки работы со всеми HTTP-библиотеками, от базового urllib до продвинутого requests. Вы научитесь выбирать оптимальный инструмент для конкретных задач и писать эффективный, профессиональный код, востребованный на рынке.
История и эволюция HTTP-библиотек в Python
История HTTP-библиотек в Python отражает эволюцию самого языка и веб-технологий в целом. Начиналось всё с базового модуля urllib, который появился ещё в Python 1.x. Этот первопроходец предлагал минимальный набор функций для работы с URL, но уже тогда было ясно — Python нуждается в более мощных инструментах для веб-взаимодействия.
С выходом Python 2.x появился urllib2 — значительный шаг вперёд, предложивший более гибкий API и расширенные возможности для работы с HTTP-запросами. urllib2 добавил поддержку аутентификации, перенаправлений и cookies. Однако сложность API и неочевидный синтаксис оставляли желать лучшего.
Алексей Петров, технический директор
Когда я начинал работать с Python в 2005 году, urllib2 был единственным разумным выбором для проекта, где требовалось интегрироваться с десятком внешних API. Помню, как мы писали обёртки вокруг urllib2, чтобы сделать код читабельнее. После выхода requests в 2011 году, мы потратили две недели на миграцию и сократили кодовую базу на 30%. Особенно впечатлила элегантность работы с JSON и обработка ошибок. После этого случая я всегда советую начинающим разработчикам сразу учиться с requests, а к urllib обращаться только при особой необходимости.
Urllib3 появился в 2008 году как независимый проект, созданный Андреем Петровым. Эта библиотека предложила поддержку пулов соединений, что значительно улучшило производительность при множественных запросах к одному хосту, а также более удобный интерфейс для загрузки файлов.
Настоящая революция произошла в 2011 году с появлением requests от Кеннета Рейтца. Эта библиотека быстро стала стандартом де-факто благодаря интуитивному API, встроенной поддержке JSON и множеству других возможностей, которые сделали написание HTTP-кода значительно проще.
| Библиотека | Год появления | Python версия | Ключевые нововведения |
|---|---|---|---|
| urllib | 1996 | 1.x | Базовая работа с URL, минимальный функционал |
| urllib2 | 2001 | 2.x | Поддержка аутентификации, cookies, custom headers |
| urllib3 | 2008 | 2.x+ | Пулы соединений, удобная загрузка файлов, thread-safety |
| requests | 2011 | 2.x, 3.x | Интуитивный API, автоматическая обработка JSON, сессии |
В Python 3 библиотеки urllib и urllib2 были реорганизованы и объединены под общим пространством имён urllib, разделённым на подмодули: urllib.request, urllib.error, urllib.parse и urllib.robotparser. Это изменение, хотя и улучшило организацию кода, не сделало API значительно удобнее.
Сегодня ландшафт HTTP-библиотек Python включает как эти классические инструменты, так и более современные асинхронные решения, такие как aiohttp и httpx, отвечающие потребностям высокопроизводительных приложений в эпоху async/await.

Ключевые отличия urllib и urllib2: синтаксис и возможности
Понимание различий между urllib и urllib2 важно не только для тех, кто поддерживает legacy-код, но и для осознания того, как эволюционировали HTTP-библиотеки в Python. Эти два модуля представляют собой разные эпохи в подходе к работе с веб-запросами.
Начнем с синтаксиса. В urllib основной функцией для открытия URL является urlopen(), которая возвращает файлоподобный объект:
import urllib
response = urllib.urlopen('http://example.com')
html = response.read()
В urllib2 подход немного отличается. Здесь также есть функция urlopen(), но она обеспечивает более гибкий контроль над запросами через механизм Request объектов:
import urllib2
request = urllib2.Request('http://example.com')
response = urllib2.urlopen(request)
html = response.read()
Ключевые различия между этими библиотеками не ограничиваются только синтаксисом:
- Обработка ошибок: urllib2 предлагает более детальную обработку HTTP-ошибок через специальные исключения (HTTPError, URLError).
- Поддержка прокси: хотя обе библиотеки поддерживают прокси-серверы, urllib2 делает это более элегантно через ProxyHandler.
- Аутентификация: urllib2 имеет встроенную поддержку базовой и дайджест-аутентификации через AuthHandler.
- Cookies: urllib2 поддерживает cookies через CookieHandler, в то время как в urllib для этого требуются дополнительные манипуляции.
Посмотрим на пример отправки POST-запроса в обеих библиотеках:
# urllib
import urllib
params = urllib.urlencode({'key': 'value'})
response = urllib.urlopen('http://example.com', params)
# urllib2
import urllib2
import urllib
params = urllib.urlencode({'key': 'value'})
request = urllib2.Request('http://example.com', data=params)
response = urllib2.urlopen(request)
Очевидно, что urllib2 требует больше кода, но предоставляет более гибкий контроль. Эта гибкость особенно заметна при работе с заголовками запросов:
# urllib2
import urllib2
request = urllib2.Request('http://example.com')
request.add_header('User-Agent', 'Mozilla/5.0')
response = urllib2.urlopen(request)
В urllib добавление заголовков требует значительно больше кода и зачастую нестандартных подходов.
В Python 3 обе библиотеки были переработаны и объединены в пакет urllib с подмодулями:
# Python 3
import urllib.request
import urllib.parse
data = urllib.parse.urlencode({'key': 'value'}).encode('utf-8')
req = urllib.request.Request('http://example.com', data=data)
with urllib.request.urlopen(req) as response:
html = response.read()
| Функциональность | urllib | urllib2 | urllib в Python 3 |
|---|---|---|---|
| GET-запросы | Простой API | Через Request объект | urllib.request.urlopen |
| POST-запросы | Ограниченная поддержка | Полная поддержка | urllib.request.Request с data |
| Обработка ошибок | Базовая | Расширенная (HTTPError) | urllib.error |
| Cookies | Требуется дополнительный код | Через CookieHandler | http.cookiejar |
| Редиректы | Автоматические | Настраиваемые | Настраиваемые через urllib.request |
Несмотря на все улучшения в urllib2 и последующую реорганизацию в Python 3, эти библиотеки всё ещё остаются сложными для новичков и требуют написания шаблонного кода для решения типичных задач. Это стало одной из главных причин создания более высокоуровневых альтернатив — urllib3 и requests.
Urllib3 и requests: современный подход к HTTP-запросам
Переход от urllib2 к urllib3 и requests — это как пересадка с механической пишущей машинки на современный ноутбук. Оба инструмента значительно упрощают работу с HTTP, но делают это по-разному. 🔄
Urllib3 появился как проект, решающий конкретную проблему: эффективное управление соединениями. Он предлагает пулы соединений, что критично для высоконагруженных приложений, работающих с множеством API-эндпоинтов. В отличие от своих предшественников, urllib3 также обеспечивает thread-safety, что делает его идеальным для многопоточных приложений.
import urllib3
http = urllib3.PoolManager()
response = http.request('GET', 'http://example.com')
data = response.data.decode('utf-8')
Requests, в свою очередь, делает ставку на простоту и элегантность. Кеннет Рейтц создал библиотеку, следуя принципу «человеческий интерфейс для HTTP». Requests абстрагирует сложности HTTP и предоставляет интуитивно понятный API, который позволяет сосредоточиться на бизнес-логике, а не на деталях протокола.
import requests
response = requests.get('http://example.com')
data = response.text
Ключевые преимущества requests перед предшественниками:
- Автоматическая десериализация JSON: получите данные в виде Python-словаря одной командой
- Встроенная поддержка сессий: сохраняйте cookies, параметры и настройки между запросами
- Поддержка международных доменов: работайте с Unicode URL без дополнительных преобразований
- Элегантная обработка multipart-запросов: загружайте файлы без сложного формирования запросов
- Встроенные возможности аутентификации: поддержка Basic, Digest, OAuth и других механизмов
Михаил Соколов, ведущий разработчик
Работал над проектом аналитики для маркетплейса, где нам требовалось обрабатывать данные из десятков API. Изначально использовали urllib3 из-за производительности — нам нужно было делать около 100,000 запросов в день. Всё шло хорошо до момента, когда пришлось интегрироваться с API, использующим сложную OAuth-аутентификацию с подписыванием запросов. Код с urllib3 превратился в монстра из 200+ строк. Решили попробовать requests и были поражены: та же функциональность уместилась в 20 строк, при этом производительность не пострадала, так как requests использует urllib3 под капотом. Выводы сделали очевидные: requests для всех новых интеграций, urllib3 оставили только там, где требовались особые оптимизации пулов соединений.
Библиотека requests действительно использует urllib3 в качестве основы, добавляя к ней удобный интерфейс. Это означает, что вы получаете преимущества urllib3 (пулы соединений, высокая производительность) вместе с удобством requests.
Сравним синтаксис для типичных задач:
# POST-запрос с JSON-данными
# urllib3
import urllib3
import json
http = urllib3.PoolManager()
encoded_data = json.dumps({"key": "value"}).encode('utf-8')
response = http.request(
'POST',
'http://example.com/api',
body=encoded_data,
headers={'Content-Type': 'application/json'}
)
# requests
import requests
response = requests.post(
'http://example.com/api',
json={"key": "value"}
)
Разница очевидна: requests избавляет от необходимости кодирования данных, ручного указания Content-Type и прочих низкоуровневых деталей.
Еще один пример — загрузка файла:
# urllib3
import urllib3
from urllib3.fields import RequestField
from urllib3.filepost import encode_multipart_formdata
http = urllib3.PoolManager()
with open('file.txt', 'rb') as fp:
file_data = fp.read()
field = RequestField(
name='file',
data=file_data,
filename='file.txt'
)
fields = [field]
content_type, body = encode_multipart_formdata(fields)
response = http.request(
'POST',
'http://example.com/upload',
body=body,
headers={'Content-Type': content_type}
)
# requests
import requests
with open('file.txt', 'rb') as fp:
response = requests.post(
'http://example.com/upload',
files={'file': fp}
)
Код с использованием requests не только короче, но и значительно понятнее. 📊
Производительность и безопасность: что важнее для проекта
Решая вопрос выбора HTTP-библиотеки, многие разработчики попадают в ловушку: фокусируются исключительно на удобстве API, игнорируя критичные параметры — производительность и безопасность. А ведь именно эти факторы часто определяют долгосрочный успех проекта. 🔒
Начнем с производительности. В идеальном мире мы хотим и удобный API, и максимальную скорость, но реальность требует компромиссов. Сравним время выполнения одинаковых операций:
| Операция | urllib (мс) | urllib2 (мс) | urllib3 (мс) | requests (мс) |
|---|---|---|---|---|
| Простой GET-запрос | 15 | 17 | 14 | 18 |
| 10 последовательных GET-запросов | 150 | 170 | 75 | 85 |
| POST с 1MB данных | 45 | 48 | 35 | 40 |
| Загрузка файла (10MB) | 420 | 390 | 280 | 295 |
Цифры условные и могут варьироваться в зависимости от сетевых условий, но тренд очевиден: urllib3 показывает наилучшую производительность, особенно при множественных запросах, благодаря пулам соединений. Requests незначительно уступает, поскольку добавляет тонкий слой абстракции над urllib3.
Интересно, что для простых одиночных запросов все библиотеки демонстрируют схожие результаты. Разница проявляется при масштабировании:
- Для приложений с редкими HTTP-запросами: производительность не имеет решающего значения, выбирайте исходя из удобства API
- Для скрейперов и краулеров: urllib3 или requests с правильно настроенными сессиями дадут ощутимый прирост
- Для микросервисов с высокой нагрузкой: urllib3 может предложить наилучший контроль над пулами соединений
Теперь о безопасности. В эпоху постоянных угроз этот аспект нельзя игнорировать. Все четыре библиотеки поддерживают базовые протоколы шифрования, но различаются по уровню защиты от типичных уязвимостей:
- SSL-верификация: urllib и urllib2 требуют дополнительного кода для правильной проверки сертификатов, urllib3 и requests включают это "из коробки"
- Защита от MITM-атак: requests и urllib3 предлагают лучшие механизмы защиты
- SNI-поддержка: urllib3 и requests поддерживают, urllib и urllib2 — с ограничениями
- Защита от уязвимостей перенаправления: requests предлагает лучший контроль
Важный момент: безопасность библиотеки определяется не только её внутренним кодом, но и регулярностью обновлений. Здесь requests и urllib3 в явном преимуществе, так как активно поддерживаются.
# requests с акцентом на безопасность
import requests
session = requests.Session()
session.verify = '/path/to/certfile' # Или True для проверки по системным сертификатам
session.cert = ('/path/to/client.cert', '/path/to/client.key')
session.max_redirects = 5 # Ограничение перенаправлений
response = session.get('https://example.com')
Для многих современных проектов баланс между производительностью и безопасностью критически важен. Именно поэтому urllib3 и requests стали предпочтительным выбором: они предлагают оптимальное соотношение этих параметров вместе с удобным API.
При выборе библиотеки для конкретного проекта учитывайте:
- Требования к производительности (число запросов в секунду, объемы данных)
- Чувствительность передаваемых данных и необходимость повышенной безопасности
- Долгосрочную поддержку и обновления библиотеки
- Интеграцию с другими компонентами системы
Правильная оценка этих факторов позволит сделать выбор, который будет оптимален не только сегодня, но и через несколько лет, когда проект вырастет.
Практический выбор: когда какую библиотеку использовать
Теория хороша, но давайте перейдем к практике: когда конкретно стоит использовать каждую из библиотек? Правильный выбор инструмента для конкретной задачи — это то, что отличает опытного разработчика от новичка. 🧰
Когда использовать urllib/urllib2:
- Работа с legacy-кодом, который уже использует эти библиотеки
- Ограничения на внешние зависимости (например, в embedded-системах)
- Необходимость минимальных зависимостей в стандартной библиотеке Python
- Образовательные цели — понимание низкоуровневых механизмов HTTP
Пример использования urllib в ситуации с ограниченными зависимостями:
# Python 3, только стандартная библиотека
import urllib.request
import urllib.parse
import json
def fetch_api_data(endpoint, params=None):
base_url = "https://api.example.com/v1/"
url = base_url + endpoint
if params:
url += "?" + urllib.parse.urlencode(params)
try:
with urllib.request.urlopen(url) as response:
return json.loads(response.read().decode('utf-8'))
except urllib.error.HTTPError as e:
print(f"HTTP Error: {e.code} – {e.reason}")
except urllib.error.URLError as e:
print(f"URL Error: {e.reason}")
return None
Когда использовать urllib3:
- Проекты с высокими требованиями к производительности
- Необходимость тонкого контроля над пулами соединений
- Когда нужны низкоуровневые возможности, но с современным API
- Работа в многопоточной среде, где важна thread-safety
- Необходимость выполнения множества запросов к одному хосту
Пример эффективного использования urllib3 для параллельных запросов:
import urllib3
import json
from concurrent.futures import ThreadPoolExecutor
def fetch_user(user_id):
http = urllib3.PoolManager()
response = http.request('GET', f'https://api.example.com/users/{user_id}')
if response.status == 200:
return json.loads(response.data.decode('utf-8'))
return None
# Параллельная загрузка данных для 100 пользователей
with ThreadPoolExecutor(max_workers=10) as executor:
user_ids = range(1, 101)
results = list(executor.map(fetch_user, user_ids))
active_users = [user for user in results if user and user.get('status') == 'active']
Когда использовать requests:
- Большинство стандартных веб-проектов и интеграций с API
- Когда приоритет — скорость разработки и читаемость кода
- Для работы с REST API, особенно с JSON-данными
- При необходимости сложной аутентификации (OAuth, JWT)
- Для учебных проектов и быстрых прототипов
- Когда важна хорошая документация и большое сообщество
Пример использования requests для интеграции с REST API:
import requests
class ApiClient:
def __init__(self, base_url, api_key):
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def get_resource(self, endpoint, params=None):
url = f"{self.base_url}/{endpoint}"
response = self.session.get(url, params=params)
response.raise_for_status() # Бросает исключение при ошибках HTTP
return response.json()
def create_resource(self, endpoint, data):
url = f"{self.base_url}/{endpoint}"
response = self.session.post(url, json=data)
response.raise_for_status()
return response.json()
# Использование
client = ApiClient("https://api.example.com/v2", "your-api-key")
users = client.get_resource("users", {"status": "active"})
new_user = client.create_resource("users", {
"name": "John Doe",
"email": "john@example.com"
})
Для выбора оптимальной библиотеки полезно задать себе несколько ключевых вопросов:
- Насколько критична производительность для моего проекта?
- Какова сложность HTTP-взаимодействий, которые мне нужны?
- Важнее скорость разработки или контроль над всеми аспектами?
- Есть ли ограничения на внешние зависимости?
- Кто будет поддерживать этот код в будущем?
В современной Python-разработке requests стал стандартом де-факто, и для большинства проектов это оптимальный выбор. Однако всегда полезно знать альтернативы и понимать их сильные стороны — это позволит вам делать осознанный выбор, а не просто следовать тренду.
Помните, что в Python-экосистеме появляются и новые библиотеки, такие как httpx (HTTP-клиент с поддержкой async/await) или aiohttp (асинхронный HTTP-клиент). Для современных высоконагруженных приложений, использующих асинхронное программирование, эти инструменты могут предложить значительные преимущества перед традиционными синхронными библиотеками.
Выбор HTTP-библиотеки в Python — это баланс между удобством, производительностью и безопасностью. Requests остается золотым стандартом для большинства сценариев, предлагая интуитивный интерфейс без существенных компромиссов. Urllib3 — мощный выбор для высоконагруженных систем с акцентом на производительность. Стандартные urllib модули сохраняют свою нишу в среде с ограниченными зависимостями. Ключ к правильному решению — честная оценка реальных требований вашего проекта, а не слепое следование популярности инструмента. Вооружившись пониманием сильных и слабых сторон каждой библиотеки, вы сможете создавать эффективный, безопасный и поддерживаемый код.