В данной статье мы опишем библиотеку языка Python urllib3. Мы расскажем, как получать данные, публиковать их и передавать в потоковом режиме, работать с JSON и использовать редирект.
Протокол передачи гипертекста (HTTP) — это прикладной протокол для распределенных, совместимых с гипермедиа, информационных систем. HTTP является основой основ передачи данных для World Wide Web.
Что такое urllib3
Библиотека urllib3 — это мощный HTTP-клиент на Python c простым для понимания и продуманным кодом. Она поддерживает безопасность потоков, пул соединений, проверку SSL / TLS на стороне клиента, загрузку файлов с многокомпонентным кодированием, создание повторных запросов и работу с редиректом HTTP, архивирование и разархивирование, а также прокси для HTTP и SOCKS.
Установка библиотеки при помощи пакетного менеджера pip:
$ pip install urllib3
Проверка версии библиотеки
Для начала мы выведем на экран версию библиотеки urllib3.
# version.py #!/usr/bin/env python3 import urllib3 rint(urllib3.__version__)
Эта несложная программа позволяет нам узнать версию установленной нами библиотеки urllib3:
1.24.1
Проверка состояния запроса
HTTP-коды состояния показывают нам, был ли HTTP-запрос успешно обработан, и если нет, то по какой причине. Коды ответов делятся на пять групп:
- информационные (100–199)
- успешные (200–299)
- редирект (300–399)
- ошибка клиента (400–499)
- ошибка сервера (500–599)
# status.py
#!/usr/bin/env python3
import urllib3
http = urllib3.PoolManager()
url = 'http://webcode.me'
resp = http.request('GET', url)
print(resp.status)
В этом примере мы генерируем GET-запрос на сайт webcode.me и выводим на экран код ответа от сайта.
Для начала мы создаем менеджер соединений при помощи функции urllib3.PoolManager() и сохраняем его в переменную http. Он обрабатывает все детали пула соединений и обеспечивает безопасность потоков.
Далее мы записываем в переменную url строку с адресом сайта ('http://webcode.me').
Затем мы шлем на этот адрес GET-запрос при помощи метода request() и сохраняем результат в переменную resp. После этого выводим статус запроса на экран:
200
Код ответа 200 означает, что запрос прошел успешно.
GET-запросы в urllib3
В протоколе HTTP метод GET запрашивает представление страницы по адресу, определенному в запросе .
# get_request.py
#!/usr/bin/env python3
import urllib3
http = urllib3.PoolManager()
url = 'http://webcode.me'
resp = http.request('GET', url)
print(resp.data.decode('utf-8'))
В данном примере, как и в предыдущем, мы посылаем GET-запрос по адресу webcode.me. Но сейчас мы выводим на экран не код ответа, а сам результат запроса, то есть html-код запрошенной страницы.
Генерируем GET-запрос и записываем результат в переменную resp. Далее мы берем данные из этой переменной, декодируем их и выводим на экран: print(resp.data.decode('utf-8')). В результате получаем:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My html page</title>
</head>
<body>
<p>
Today is a beautiful day. We go swimming and fishing.
</p>
<p>
Hello there. How are you?
</p>
</body>
HEAD-запросы в urllib3
HEAD-запрос полностью идентичен GET-запросу, за исключением того, что в нем не запрашивается тело страницы. Обычно он используется для извлечения метаданных.
head_request.py
#!/usr/bin/env python3
import urllib3
http = urllib3.PoolManager()
url = 'http://webcode.me'
resp = http.request('HEAD', url)
print(resp.headers['Server'])
print(resp.headers['Date'])
print(resp.headers['Content-Type'])
print(resp.headers['Last-Modified'])
В данном примере мы создаем HEAD-запрос на сайт webcode.me. Переменная, содержащая объект ответа, представляет собой словарь, из которого можно извлечь различные данные:
print(resp.headers['Server']) print(resp.headers['Date']) print(resp.headers['Content-Type']) print(resp.headers['Last-Modified'])
Результат будет следующим:
nginx/1.6.2 Thu, 20 Feb 2020 14:35:14 GMT text/html Sat, 20 Jul 2019 11:49:25 GMT
Из этого ответа можно заключить, что сервер, на котором работает этот сайт, — nginx, а тип контента — html-код.
HTTPS-запросы
Библиотека Urllib3 обеспечивает проверку TLS / SSL на стороне клиента. Но для этого нам нужно предварительно установить библиотеку certifi. Она представляет собой тщательно отобранную коллекцию корневых сертификатов для проверки достоверности SSL-сертификатов при проверке подлинности хостов TLS. Эта библиотека была выделена в отдельный модуль из известного проекта Requests (который также является очень известной библиотекой для протокола HTTP)
$ pip install certifi
При помощи приведенной выше команды мы установили библиотеку certifi.
import certifi print(certifi.where())
Для ссылки на установленный модуль центра сертификации (CA) мы используем встроенную функцию where().
# status2.py
#!/usr/bin/env python3
import urllib3
import certifi
url = 'https://httpbin.org/anything'
http = urllib3.PoolManager(ca_certs=certifi.where())
resp = http.request('GET', url)
print(resp.status
Чтобы отправить GET- запрос на страницу https://httpbin.org/anything, мы должны передать пакет корневых сертификатов в функцию PoolManager(). Без этого сервер вернет следующее предупреждение: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. («Поступил непроверенный HTTPS-запрос. Крайне рекомендуется добавить сертификат проверки»).
Параметры запроса
Параметры запроса являются частью URL (унифицированного указателя ресурса), который присваивает значения указанным параметрам. Это один из способов отправки данных на сервер назначения.
http://example.com/api/users?name=John%20Doe&occupation=gardener
Параметры запроса указываются после знака '?', а поля разделяются знаком '&'. Специальные символы, например пробелы, кодируются определенным образом. В приведенном выше примере пробелы закодированы значением %20.
# query_params.py
#!/usr/bin/env python3
import urllib3
import certifi
http = urllib3.PoolManager(ca_certs=certifi.where())
payload = {'name': 'Peter', 'age': 23}
url = 'https://httpbin.org/get'
req = http.request('GET', url, fields=payload)
print(req.data.decode('utf-8'))
В данном примере мы послали GET-запрос с некоторыми параметрами на страницу https://httpbin.org/get . Эта страница просто возвращает определенные данные обратно клиенту, включая при этом параметры запроса. Этот сайт вообще создан для тестирования HTTP-запросов.
payload = {'name': 'Peter', 'age': 23}
Параметры запросы были заданы в формате словаря, который был сохранен в переменную payload.
req = http.request('GET', url, fields=payload)
А при помощи необязательного аргумента fields они были переданы в функцию request().
{
"args": {
"age": "23",
"name": "Peter"
},
"headers": {
"Accept-Encoding": "identity",
"Host": "httpbin.org",
"X-Amzn-Trace-Id": "Root=1-5e4ea45f-c3c9c721c848f8f81a3129d8"
},
"origin": "188.167.251.9",
"url": "https://httpbin.org/get?name=Peter&age=23"
}
Ответ сайта httpbin.org был дан в виде строки в формате JSON, в начале которой стоят параметры нашего запроса.
POST-запросы
В протоколе HTTP метод POST используется для передачи данных на сервер. Он часто применяется для загрузки файлов или для заполнения веб-форм.
# post_request.py
#!/usr/bin/env python3
import urllib3
import certifi
http = urllib3.PoolManager(ca_certs=certifi.where())
url = 'https://httpbin.org/post'
req = http.request('POST', url, fields={'name': 'John Doe'})
print(req.data.decode('utf-8'))
В данном примере был послан POST-запрос. Его данные были заданы при помощи аргумента fields.
А ответ сервера был таким:
{
"args": {},
"data": "",
"files": {},
"form": {
"name": "John Doe"
},
...
"url": "https://httpbin.org/post"
}
Отправка данных в формате JSON
В запросах, таких как POST или PUT, клиент сообщает серверу, какой тип данных фактически будет отправлен при помощи поля 'Content-Type' в параметре headers.
# send_json.py
#!/usr/bin/env python3
import urllib3
import certifi
import json
http = urllib3.PoolManager(ca_certs=certifi.where())
payload = {'name': 'John Doe'}
encoded_data = json.dumps(payload).encode('utf-8')
resp = http.request(
'POST',
'https://httpbin.org/post',
body=encoded_data,
headers={'Content-Type': 'application/json'})
data = json.loads(resp.data.decode('utf-8'))['json']
print(data)
В данном примере мы отправили данные в формате JSON.
payload = {'name': 'John Doe'}
encoded_data = json.dumps(payload).encode('utf-8')
Здесь мы перевели данные в формате JSON в двоичный формат.
resp = http.request(
'POST',
'https://httpbin.org/post',
body=encoded_data,
headers={'Content-Type': 'application/json'})
Тут мы задали тип передаваемых данных в поле 'Content-Type' параметра headers.
data = json.loads(resp.data.decode('utf-8'))['json']
print(data)
И в конце мы декодировали в текстовый формат данные, возвращенные сервером, и вывели их на экран.
Использование двоичных данных
В следующем примере мы загружаем с сервера двоичные данные.
get_binary.py
#!/usr/bin/env python3
import urllib3
http = urllib3.PoolManager()
url = 'http://webcode.me/favicon.ico'
req = http.request('GET', url)
with open('favicon.ico', 'wb') as f:
f.write(req.data)
В данном примере с сервера загружается маленькая иконка.
with open('favicon.ico', 'wb') as f:
f.write(req.data)
req.data имеет двоичный формат, так что мы можем записывать его прямо на диск.
Работа с потоковыми данными
Кодирование данных по частям — это механизм передачи потоковых данных, доступный начиная с версии HTTP 1.1. При кодировании с частичной передачей поток данных делится на ряд непересекающихся блоков. Эти блоки отправляются и принимаются независимо друг от друга. Перед отправкой блока всегда посылается его размер в байтах.
Установка параметра preload_content в состояние False означает, что библиотека urllib3 будет производить потоковою передачу данных. Метод stream() выполняет итерации по частям содержимого ответа. При потоковой передаче мы должны вызывать метод release_conn(), который переносит http-соединение обратно в пул соединений, чтобы его можно было использовать повторно.
# streaming.py
#!/usr/bin/env python3
import urllib3
import certifi
url = "https://docs.oracle.com/javase/specs/jls/se8/jls8.pdf"
local_filename = url.split('/')[-1]
http = urllib3.PoolManager(ca_certs=certifi.where())
resp = http.request(
'GET',
url,
preload_content=False)
with open(local_filename, 'wb') as f:
for chunk in resp.stream(1024):
f.write(chunk)
resp.release_conn()
В данном примере мы загружаем с сервера PDF-файл.
resp = http.request(
'GET',
url,
preload_content=False)
Задав параметр preload_content=False, мы включаем потоковую передачу данных.
with open(local_filename, 'wb') as f:
for chunk in resp.stream(1024):
f.write(chunk)
Мы производим итерацию по блокам данных и записываем их в файл.
resp.release_conn()
В конце мы освобождаем соединение для дальнейшего использования.
Редирект
Редирект переадресовывает пользователей и поисковые системы на разные URL-адреса, отличные от тех, которые они первоначально запрашивали. Чтобы разрешить перенаправление нашего запроса, мы присваиваем параметру redirect значение True.
# redirect.py
#!/usr/bin/env python3
import urllib3
import certifi
http = urllib3.PoolManager(ca_certs=certifi.where())
url = 'https://httpbin.org/redirect-to?url=/'
resp = http.request('GET', url, redirect=True)
print(resp.status)
print(resp.geturl())
print(resp.info())
В данном примере мы создали запрос с разрешением редиректа. А вот результат выполнения нашего запроса:
200
/
HTTPHeaderDict({'Date': 'Fri, 21 Feb 2020 12:49:29 GMT', 'Content-Type': 'text/html;
charset=utf-8', 'Content-Length': '9593', 'Connection': 'keep-alive',
'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true'})
Пример работы библиотеки urllib3 с фреймворком Flask
В данном примере мы пошлем запрос на небольшое веб-приложение, запущенное на сервере flask. Подробнее о фреймворке flask можно прочитать, например, в нашем обзоре.
Для начала установим библиотеку flask:
$ pip install flask
# app.py
#!/usr/bin/env python3
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/headers')
def hello():
ua = request.headers.get('user-agent')
ka = request.headers.get('connection')
return f'User agent: {ua}; Connection: {ka}'
Данное приложение на фреймворке flask выполняет только одну операцию: в ответ на запрос оно возвращает пользователю заголовки этого самого запроса.
# send_req.py
#!/usr/bin/env python3
import urllib3
http = urllib3.PoolManager()
url = 'localhost:5000/headers'
headers = urllib3.make_headers(keep_alive=True, user_agent='Python program')
resp = http.request('GET', url, headers=headers)
print(resp.data.decode('utf-8'))
В данном коде мы посылаем запрос нашему серверному приложению на базе библиотеки flask.
url = 'localhost:5000/headers'
По умолчанию, flask сервер запускается на 5000 порте.
headers = urllib3.make_headers(keep_alive=True, user_agent='Python program')
При помощи метода make_headers() мы создаем словарь заголовков.
resp = http.request('GET', url, headers=headers)
Посылаем запрос по заданному ранее URL.
print(resp.data.decode('utf-8'))
Выводим ответ на дисплей.
$ export FLASK_APP=app.py $ flask run
Таким образом мы запускаем приложение flask.
$ ./send_req.py User agent: Python program; Connection: keep-alive
А с другого терминала мы запускаем программу запроса и получаем от сервера ответ.
Итак, мы вкратце рассмотрели библиотеку Python urllib3.
Перевод статьи «Python urllib3».

