Работа с REST API на Python
В этой статье вы узнаете как выполнять запросы к REST API на Python 3 и обрабатывать ответы.
Если ваша цель — создание своего REST API — переходите к статье «Flask»
Прежде чем что-то устанавливать убедитесь, что вы знакомы с работой в виртуальном окружении Python.
Прочитать об этом можно в статье «Виртуальные окружения в Python»
Подготовка
Активируйте ваше виртуальное окружение и установите requests командой
python -m pip install requests
Изучите список установленных модулей
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)
Запустите скрипт командой
Если получили 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)
Если всё прошло успешно, то получите
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 и найдите первое фото велосипеда.
Теперь измените код так, чтобы сохранить изображение в файл
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)
Если всё прошло успешно, в вашей папке появится следующее фото
GET с параметрами
Для проверки ваших навыков работы с REST API можно воспользоваться сайтом httpbin.org
Он будет возвращать вам обратно ваш запрос.
Параметры можно записать сразу после url за знаком вопроса, но надёжнее оформить их в виде отдельного словаря (dictionary).
import requests payload = r = requests. get ( «https://httpbin.org/get» , params= payload) print (f»url: \n\ntext: \n «)
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 = r = requests. post (‘https://httpbin.org/post’, data= payload) print (f»url: \n\ntext: \n «)
Ответы, которые мы получили с httpbin приходили в формате json.
Для работы c json бывает удобно использовать встроенный метод .json()
В этом случае данные записываются в словарь и к ним очень легко обращаться.
Обратите внимание на «form» данные, которые были переданы возвращаются в этом поле.
Изменим код так, чтобы на экран выводились только эти данные
import requests payload = r = requests. post (‘https://httpbin.org/post’, data= payload) r_dict = r. json () print (r_dict[‘form’])
PUT
Всё аналогично POST просто замените post на put
Рассмотрим пример, в котором нужно передать в теле запроса json, а также использовать имеющийся токен для авторизации
import requests import json url = «http://urn.su» new_access_token = «sdlfjsljglkjfd;lkgjdlkhjlkjgdlkhjlkdjglkj» body = «»»[]»»» 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)
Обратите внимание на следующие моменты
- При передаче JSON нужно обязательно указать заголовок ‘Content-type’: ‘application/json’
- Токен передаётся с помощью Bearer
- Можно легко преобразовать данные в JSON с помощью json.loads()
Обработка ответа
Рассмотрим приёмы, которые пригодятся при работе с полученными данными
Простейший ответ сервера, например 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: » )
Очень часто данные приходят в формате json.
Например, нужно извлечь из них токен, который хранится в access_token
Чтобы его получить, воспользуйтесь методом json() который возвращает словарь ().
И из этого словаря получите токен по ключевому слову 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 делать не обязательно. Это я для примера показываю, что можно отсортировтаь ключевые слова.
Вытащить часть ответа
Часто интерес бывает не весь ответ, а только часть. О том как её грамотно выделить из ответа читайте в статье
Аутентификация
Рассмотрим базовую аутентификацию на сайте httpbin
Придумайте любое имя пользоватлея и пароль к нему.
Я придумал andrey с паролем heihei
Перейдите на httpbin.org . Убедитесь, что в адресной строке стоит basic-auth/andrey/heihei либо те логин и пароль, что придумали вы.
Введите ваши логин и пароль
Убедитесь, что аутентификация прошла успешно
Теперь проделаем такую же аутентификацию с помощью Python
Создайте файл auth_demo.py со следующим кодом
import requests r = requests. get ( ‘https://httpbin.org/basic-auth/andrey/heihei’ , auth= ( ‘andrey’ , ‘heihei’ ) ) print (r.text)
Ответ совпадает с тем что мы уже получали в браузере
Выполните такой же запрос, но с неправильным паролем. Убедитесь в том, что text ничего не содержит. Замените print (r.text) на print (r) и убедитесь, что полученный объект это
Задержка
Часто бывает нужно ограничить время ожидания ответа. Это можно сделать с помощью параметра 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)
Задержка равна одной секунде. А ждать ответ можно до трёх секунд.
Измените код так, чтобы ответ приходил заведомо позже чем наш таймаут в три секунды.
import requests r = requests. get (‘https://httpbin.org/delay/7’, timeout= 3) print (r)
Задержка равна семи секундам. А ждать ответ можно по-прежнему только до трёх секунд.
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 «», 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 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)
Response is taking too long. HTTPSConnectionPool(host=’httpbin.org’, port=443): Read timed out. (read timeout=3)
Работа с сертификатами
В Python по умолчанию используются не сертификаты системы, а сертификаты из модуля certif