В данной статье мы опишем библиотеку языка 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».