Работа с REST API на Python

Содержание
Введение
Подготовка
GET
GET с параметрами
POST
PUT с токеном
Обработка ответа сервера
json.dumps(): вывести читаемый json
Вытащить часть ответа
Аутентификация
Задержка
Поддержка сертификатов
Похожие статьи

Введение

В этой статье вы узнаете как выполнять запросы к REST API на Python 3 и обрабатывать ответы.

Если ваша цель - создание своего REST API - переходите к статье «Flask»

Прежде чем что-то устанавливать убедитесь, что вы знакомы с работой в виртуальном окружении Python.

Прочитать об этом можно в статье «Виртуальные окружения в Python»

Подготовка

Активируйте ваше виртуальное окружение и установите requests командой

python -m pip install requests

Изучите список установленных модулей

python -m pip list

Package Version ---------- --------- certifi 2020.6.20 chardet 3.0.4 idna 2.10 pip 20.2.3 requests 2.24.0 setuptools 50.3.1 urllib3 1.25.10 wheel 0.35.1

requests подтягивает за собой requests, certifi , chardet, idna, urllib3

Проверить куда установился requests в этом окружении можно командой

python3 -m pip show requests

Name: requests Version: 2.24.0 Summary: Python HTTP for Humans. Home-page: https://requests.readthedocs.io Author: Kenneth Reitz Author-email: me@kennethreitz.org License: Apache 2.0 Location: /home/andrei/python/virtualenvs/answerit_env/lib/python3.8/site-packages Requires: certifi, chardet, urllib3, idna Required-by:

GET

Чтобы сделать GET запрос достаточно импортировать requests и выполнить requests.get

Создайте файл rdemo.py следующего содержания:

import requests r = requests.get('https://xkcd.com/353/') print(r)

Запустите скрипт командой

python rdemo.py

<Response [200]>

Если получили 200 значит всё хорошо. Изменим наш код, чтобы узнать, какие действия мы можем произвести с объектом <Response [200]>

dir(r) выдаст список доступных атрибутов и методов

import requests r = requests.get('https://topbicycle.ru/b/stels_pilot_950_md_26.php') print(dir(r))

['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_content', '_content_consumed', '_next', 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'is_permanent_redirect', 'is_redirect', 'iter_content', 'iter_lines', 'json', 'links', 'next', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']

Более подробную информацию можно получить заменив dir(r) на help(r)

Если вам интересно - прочитайте статью requests help

Из этого документа можно узнать http статус содержится в атрибуте status_code

import requests r = requests.get('https://topbicycle.ru/b/stels_pilot_950_md_26.php') print(r.status_code)

Если всё прошло успешно, то получите

200

ok вернёт True если ответ не 4XX или 5XX

headers возвращает заголовок ответа

Также из этого документа можно узнать что text возвращает содержимое ответа в формате unicode а content содержимое ответа в байтах

Имените код на

import requests r = requests.get('https://xkcd.com/353/') print("content:") print("-----") print(r.content) print("-----") print("text:") print("-----") print(r.text)

И изучите разницу

Обычно content используют для работы с изображениями

Перейдите на TopBicycle.ru и найдите первое фото велосипеда.

Скопируйте его url

https://topbicycle.ru/b/img/stels_pilot_950_MD_26.jpg

Теперь измените код так, чтобы сохранить изображение в файл

import requests r = requests.get("https://topbicycle.ru/b/img/stels_pilot_950_MD_26.jpg") with open("bike.jpg", "wb") as f: f.write(r.content)

Если всё прошло успешно, в вашей папке появится следующее фото

stels pilot 950 md 26 в сложенном виде изображение сайта www.TopBicycle.ru
Фото ©: TopBicycle.ru

GET с параметрами

Для проверки ваших навыков работы с REST API можно воспользоваться сайтом httpbin.org

Он будет возвращать вам обратно ваш запрос.

Изменим код rdemo.py :

Параметры можно записать сразу после url за знаком вопроса, но надёжнее оформить их в виде отдельного словаря (dictionary).

import requests payload = {'page': 2, 'count': 25} r = requests.get("https://httpbin.org/get", params=payload) print(f"url: {r.url} \n\ntext: \n {r.text}")

python rdemo.py

url: https://httpbin.org/get?page=2&count=25 text: { "args": { "count": "25", "page": "2" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "httpbin.org", "User-Agent": "python-requests/2.24.0", "X-Amzn-Trace-Id": "Root=1-5f8c2d50-4f48f16c0991ad0e6e45676e" }, "origin": "87.92.8.47", "url": "https://httpbin.org/get?page=2&count=25" }

POST

Чтобы отправить и проверить POST запрос внесите небольшие изменения в код.

Очевидно, что GET нужно заменить на POST, также params нужно заменить на data или json

data – (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the Request.

json – (optional) A JSON serializable Python object to send in the body of the Request.

import requests payload = {'website': 'heihei.ru', 'established': 2018} r = requests.post('https://httpbin.org/post', data=payload) print(f"url: {r.url} \n\ntext: \n {r.text}")

url: https://httpbin.org/post text: { "args": {}, "data": "", "files": {}, "form": { "established": "2018", "website": "heihei.ru" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "34", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "python-requests/2.24.0", "X-Amzn-Trace-Id": "Root=1-5f8c30ac-6b32899f073f9df4055c55c4" }, "json": null, "origin": "87.92.8.47", "url": "https://httpbin.org/post" }

Ответы, которые мы получили с httpbin приходили в формате json.

Для работы c json бывает удобно использовать встроенный метод .json()

В этом случае данные записываются в словарь и к ним очень легко обращаться.

Обратите внимание на "form" данные, которые были переданы возвращаются в этом поле.

Изменим код так, чтобы на экран выводились только эти данные

import requests payload = {'website': 'heihei.ru', 'established': 2018} r = requests.post('https://httpbin.org/post', data=payload) r_dict = r.json() print(r_dict['form'])

python rdemo.py

{'established': '2018', 'website': 'heihei.ru'}

PUT

Всё аналогично POST просто замените post на put

Рассмотрим пример, в котором нужно передать в теле запроса json, а также использовать имеющийся токен для авторизации

import requests import json url = "http://eth1.ru" new_access_token = "sdlfjsljglkjfd;lkgjdlkhjlkjgdlkhjlkdjglkj" body = """[{"id":"1234abc","name":"andrei","website":"eth1.ru"}]""" payload = json.loads(body) json_headers = { "Content-type": "application/json", "Authorization": "Bearer %s" %new_access_token } r = requests.put(url, headers=json_headers, json=payload, verify=False)

Обратите внимание на следующие моменты

Обработка ответа

Рассмотрим приёмы, которые пригодятся при работе с полученными данными

Простейший ответ сервера, например 200 может вообще не содержать никаких данных

response = requests.delete(url, headers=headers, verify=False) print(f"type(response): {type(response)}") print(f"response.__repr__: {repr(response)}") print(f"response.__str__: {str(response)}") print(f"response.text: {response.text}")

type(response): <class 'requests.models.Response'> response.__repr__: <Response [200]> response.__str__: <Response [200]> response.text:

Очень часто данные приходят в формате json.

Например, нужно извлечь из них токен, который хранится в access_token

Чтобы его получить, воспользуйтесь методом json() который возвращает словарь (<class 'dict'>).

И из этого словаря получите токен по ключевому слову access_token

r_dict = response.json() access_token = r_dict["access_token"]

json.dumps

Если вы хотите сразу же изучить полученный json можно воспользоваться методом dumps()

import json print(json.dumps(response.json(), indent=4))

Если вы получили не json а dict , json.dumps нужно использовать так:

import json print(json.dumps(resp.dict, indent=4, sort_keys=True))

sort_keys делать не обязательно. Это я для примера показываю, что можно отсортировтаь ключевые слова. Метод можно использовать для всех объектом, которые можно адекватно превратить в словарь.

Например, если вы хотите читаемо распечатать headers, которые имеют тип requests.structures.CaseInsensitiveDict их можно превратить в обычный словарь и применить dumps

print(json.dumps(dict(r.headers), indent=4))

{ "Access-Control-Allow-Headers": "X-Requested-With, Content-Type, Accept, Origin, Authorization", "Access-Control-Allow-Methods": "OPTIONS, POST, HEAD", "Access-Control-Allow-Origin": "", "Allow": "POST, HEAD, OPTIONS", "Content-Length": "0", "Keep-Alive": "timeout=5, max=5" }

Вытащить часть ответа

Часто интерес бывает не весь ответ, а только часть. О том как её грамотно выделить из ответа читайте в статье

«Как обратиться ко вложенным в json объектам»

Аутентификация

Рассмотрим базовую аутентификацию на сайте httpbin

Придумайте любое имя пользоватлея и пароль к нему.

Я придумал andrey с паролем heihei

Перейдите на httpbin.org . Убедитесь, что в адресной строке стоит basic-auth/andrey/heihei либо те логин и пароль, что придумали вы.

Authentication изображение с сайта www.eth1.ru

Введите ваши логин и пароль

Authentication изображение с сайта www.eth1.ru

Убедитесь, что аутентификация прошла успешно

Authentication изображение с сайта www.eth1.ru
Если Вам нужен новый ноутбук - заходите на сайт Нотик.ру

Теперь проделаем такую же аутентификацию с помощью Python

Создайте файл auth_demo.py со следующим кодом

import requests r = requests.get( 'https://httpbin.org/basic-auth/andrey/heihei', auth=('andrey', 'heihei') ) print(r.text)

python auth_demo.py

{ "authenticated": true, "user": "andrey" }

Ответ совпадает с тем что мы уже получали в браузере

Выполните такой же запрос, но с неправильным паролем. Убедитесь в том, что text ничего не содержит. Замените print(r.text) на print(r) и убедитесь, что полученный объект это

<Response [401]>

Задержка

Часто бывает нужно ограничить время ожидания ответа. Это можно сделать с помощью параметра timeout

Перейдите на httpbin.org раздел - / #/ Dynamic_data / delete_delay__delay_ и изучите документацию - если делать запрос на этот url можно выставлять время, через которое будет отправлен ответ.

Создайте файл timeout_demo.py следующего содержания

import requests r = requests.get('https://httpbin.org/delay/1', timeout=3) print(r)

Задержка равна одной секунде. А ждать ответ можно до трёх секунд.

python timeout_demo.py

<Response [200]>

Измените код так, чтобы ответ приходил заведомо позже чем наш таймаут в три секунды.

import requests r = requests.get('https://httpbin.org/delay/7', timeout=3) print(r)

Задержка равна семи секундам. А ждать ответ можно по-прежнему только до трёх секунд.

python timeout_demo.py

Traceback (most recent call last): File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 421, in _make_request six.raise_from(e, None) File "<string>", line 3, in raise_from File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 416, in _make_request httplib_response = conn.getresponse() File "/usr/lib/python3.8/http/client.py", line 1347, in getresponse response.begin() File "/usr/lib/python3.8/http/client.py", line 307, in begin version, status, reason = self._read_status() File "/usr/lib/python3.8/http/client.py", line 268, in _read_status line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") File "/usr/lib/python3.8/socket.py", line 669, in readinto return self._sock.recv_into(b) File "/usr/lib/python3/dist-packages/urllib3/contrib/pyopenssl.py", line 326, in recv_into raise timeout("The read operation timed out") socket.timeout: The read operation timed out During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3/dist-packages/requests/adapters.py", line 439, in send resp = conn.urlopen( File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 719, in urlopen retries = retries.increment( File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 400, in increment raise six.reraise(type(error), error, _stacktrace) File "/usr/lib/python3/dist-packages/six.py", line 703, in reraise raise value File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 665, in urlopen httplib_response = self._make_request( File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 423, in _make_request self._raise_timeout(err=e, url=url, timeout_value=read_timeout) File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 330, in _raise_timeout raise ReadTimeoutError( urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=3) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "timeout_demo.py", line 4, in <module> r = requests.get('https://httpbin.org/delay/7', timeout=3) File "/usr/lib/python3/dist-packages/requests/api.py", line 75, in get return request('get', url, params=params, **kwargs) File "/usr/lib/python3/dist-packages/requests/api.py", line 60, in request return session.request(method=method, url=url, **kwargs) File "/usr/lib/python3/dist-packages/requests/sessions.py", line 533, in request resp = self.send(prep, **send_kwargs) File "/usr/lib/python3/dist-packages/requests/sessions.py", line 646, in send r = adapter.send(request, **kwargs) File "/usr/lib/python3/dist-packages/requests/adapters.py", line 529, in send raise ReadTimeout(e, request=request) requests.exceptions.ReadTimeout: HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=3)

Если хотите обработать исключение - измените код используя try except

import requests try: r = requests.get('https://httpbin.org/delay/7', timeout=3) print(r) except ( requests.exceptions.Timeout, requests.exceptions.ConnectionError ) as err: print('Response is taking too long.', err)

python timeout_demo.py

Response is taking too long. HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=3)

Работа с сертификатами

В Python по умолчанию используются не сертификаты системы, а сертификаты из модуля certif

Проверить где они расположены можно следуюущим образом

python >>> import certifi >>> certifi.where()

'/home/andrei/python/venv/lib/python3.8/site-packages/certifi/cacert.pem'

Похожие статьи
Python
Интерактивный режим
dict, list, str
\: перенос строки
if, elif, else
Циклы
Функции
try except
Пакеты
ООП
Опеределить тип переменной Python
Файлы: записать, прочитать…
Работа с базами данных
datetime: Дата и время в Python
json.dumps
Сложности при работе с Python
Фреймворки: Django, Flask
socket: Python Sockets
Виртуальное окружение
subprocess: bash команды из Python
multiprocessing: несколько процессов одновременно
psutil: cистемные ресурсы
sys.argv: аргументы командной строки
PyCharm: IDE
pydantic: валидация данных
paramiko: SSH из Python
logging: запись в лог
Обучение Python
REST
Изображение баннера

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: