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

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

Introduction

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

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

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

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

Подготовка

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

python3 -m pip install requests

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

python3 -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

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

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

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

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

python3 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 / снято на Samsung

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}")

python3 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

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

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

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'])

python3 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)

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

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

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

Предположим, получены данные в формате json.

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

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

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

r_dict = r.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 делать не обязательно. Это я для примера показываю, что можно отсортировтаь ключевые слова.

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

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

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

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

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

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

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

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

Authentication image from website www.aredel.com

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

Authentication image from website www.aredel.com

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

Authentication image from website www.aredel.com
Если Вам нужен новый ноутбук - заходите на сайт Нотик.ру

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

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

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

python3 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)

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

python3 timeout_demo.py

<Response [200]>

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

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

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

python3 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) except (requests.exceptions.Timeout, requests.exceptions.ConnectionError ) as err: print('Response is taking too long.') else: print(r)

python3 timeout_demo.py

Response is taking too long.

Related Articles
Interactive mode
str: strings
\: long line wrapping
Lists []
if, elif, else
Loops
Methods
Functions
*args **kwargs
enum
Get Variable Type
Testing with Python
Calling REST API with Python
Files: write, read, append, context manager…
Download File
SQLite3: database
datetime: Date and Time в Python
json.dumps
Selenium + Python
Issues with Python
DJANGO
Flask
ZPL printer script
socket :Python Sockets
Virtual Environment
subprocess: shell commands execution
multiprocessing
psutil: system usage
sys.argv: command prompt variables
PyCharm: IDE
pydantic: data validation
paramiko: SSH with Python
enumerate

Search on this site

Subscribe to @aofeed channel for updates

Visit Channel

@aofeed

Feedbak and Questions in Telegram

@aofeedchat