В этой статье мы рассмотрим, что такое API и, в частности, REST API. Также мы разберем, что такое HTTP-запросы и какими они бывают. Кроме того, мы рассмотрим основные компоненты библиотеки requests и предоставим несколько примеров кода, которые помогут вам начать работу с ней.
Друзья, подписывайтесь на наш телеграм канал Pythonist. Там еще больше туториалов, задач и книг по Python.
Интерфейс прикладного программирования (API) — это посредник между программами; его работа заключается в том, чтобы разрешать приложениям взаимодействовать друг с другом. Эти неявные посредники постоянно появляются в нашей повседневной жизни, знаете вы об этом или нет. Например, если сегодня вы отправляли сообщение в мессенджере, то вы использовали API.
В частности, API-интерфейсы позволяют людям отправлять и получать данные с помощью кода. Однако и для извлечения данных чаще всего используются API. Например, вы можете прочитать этот пост на сайте, потому что ваш браузер получил данные, из которых состоит эта страница, с сервера Pythonist.
Но веб-серверы не отправляют данные случайным образом — это все равно, что пойти в ресторан, и официант случайно принесет вам еду. Для того чтобы сервер прислал данные, ему необходимо послать запрос. Точно так же обстоит дело и с официантом в ресторане. Итак, если вы хотите получить какие-то данные из API — вы делаете API-запрос на сервер, и он пришлет соответствующие данные.
HTTP-запросы в Python делаются при помощи библиотеки requests. Это практически стандарт. Правда, в Python также есть встроенная библиотека urllib, но питонисты, как правило, предпочитают API библиотеки requests из-за ее удобочитаемости и того факта, что она полностью поддерживает REST API. Этого мы коснемся чуть позже.
Библиотека requests изолирует все проблемы выполнения запросов с помощью простого API, что позволяет нам сосредоточиться исключительно на общении со службами и использовании данных в нашем приложении.
Мы определили, что API — это посредник между программами. Иначе его можно представить как тип программного интерфейса, который предоставляет другим приложениям доступ к определенным данным и методам.
Одной из самых популярных архитектур, используемых для создания API, является шаблон REpresentational State Transfer (REST). Архитектура REST позволяет реализовывать приложения на стороне клиента и на стороне сервера независимо друг от друга. Это означает, что код на любой стороне можно изменить, не беспокоясь о том, как это изменение повлияет на другую сторону.
Существует набор рекомендаций, предназначенных для упрощения обмена данными между программами, что делает процесс доступа к данным более простым и логичным. Не беспокойтесь, если вы не знаете этих рекомендаций: чтобы начать работу, вам не нужно знать их. Для этого вам нужно знать, как данные предоставляются службами REST.
Данные из веб-служб REST доступны в Интернете через общедоступный URL-адрес, к которому можно обращаться, отправляя HTTP-запросы.
Вернемся к нашей аналогии с рестораном. Чтобы заказать еду в ресторане, вы говорите официанту, чего бы вам хотелось. Затем официант передает вашу просьбу шеф-повару, который готовит еду и передает ее официанту, чтобы тот вернулся с ней к вам. То есть шеф-повар не будет готовить вам еду, пока не получит ваш запрос.
Интерфейсы REST API точно такие же: они прослушивают HTTP-запросы, прежде чем предпринимать какие-либо действия. Протокол HTTP — это то, что определяет набор запросов, чтобы сообщить API, какие операции выполнять для данного ресурса. Он указывает, как взаимодействовать с ресурсами, расположенными в данной конечной точке.
С REST API обычно используются следующие HTTP-запросы:
HTTP-запросы | Их описание |
GET | Получить данные |
POST | Создать данные |
PUT | Обновить существующие данные |
PATCH | Частично обновить существующие данные |
DELETE | Удалить данные |
Весьма вероятно, что вы будете выполнять HTTP-запросы GET чаще, чем любые другие методы анализа данных. Это связано с тем, что GET — самый важный метод, необходимый для получения доступа к определенным наборам данных.
Когда вы выполняете запрос к веб-серверу, его API возвращает ответ. К ответу прилагается код состояния HTTP. Код состояния дает дополнительную информацию об ответе, чтобы клиент знал тип полученного запроса.
Данные, с которыми вы взаимодействуете на веб-сервере, обозначаются URL-адресом. Подобно тому, как URL-адрес веб-страницы подключен к одной конкретной странице, URL-адрес конечной точки подключен к определенным ресурсам в API. Следовательно, конечную точку можно описать как цифровое местоположение, где API получает запросы о конкретном ресурсе на своем сервере — считайте это другим концом канала связи.
Чтобы добавить больше контекста, REST API предоставляют набор общедоступных URL-адресов, которые могут быть запрошены клиентскими приложениями для доступа к ресурсам веб-службы. Общедоступные URL-адреса, предоставляемые REST API, называются конечными точками.
API библиотеки requests позволяет разработчикам писать код для взаимодействия с REST API. Это дает им возможность отправлять HTTP-запросы с помощью Python, не беспокоясь о сложностях, которые обычно возникают при выполнении таких задач (например, ручное добавление строк запроса к URL-адресам, кодирование форм данных PUT и POST и т. д.).
Несмотря на то, что библиотека requests считается де-факто стандартом для создания HTTP-запросов в Python, она не является частью стандартной библиотеки Python и ее необходимо установить.
Наиболее естественно сделать это при помощи менеджера пакетов pip:
python -m pip install requests
Этот код представляет собой команду, которую можно запустить в терминале или в командной строке. Флаг -m
указывает интерпретатору Python запустить модуль pip как скрипт, а request
— это имя устанавливаемого пакета. После выполнения этой команды библиотека requests будет установлена и доступна для использования в среде Python.
Всегда рекомендуется управлять пакетами Python, необходимыми для различных проектов, в собственных виртуальных средах. Таким образом, пакеты для одного проекта не будут мешать и ломать системные инструменты в других проектах, так как они изолированы, а не установлены глобально.
Теперь, когда у нас установлен модуль requests, давайте посмотрим, как он работает.
Мы уже говорили, что GET — это один из наиболее распространенных HTTP-запросов, с которыми вы столкнетесь при работе с REST API. Он позволяет вам (то есть клиенту) получать данные с веб-серверов.
Важно отметить, что GET — это операция только для чтения. Это означает, что она подходит только для доступа к существующим ресурсам, но не должна использоваться для их изменения.
Чтобы продемонстрировать, как работает модуль запроса, мы будем использовать JSONPlaceholder — свободно доступный API, используемый для тестирования и прототипирования.
Рассмотрим следующий код:
import requests # Конечная точка API url = "https://jsonplaceholder.typicode.com/posts/1" # GET-запрос к API response = requests.get(url) # Выводим ответ в консоль response_json = response.json() print(response_json) # Результат """ {'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'} """
В приведенном выше коде мы сделали следующее:
request.get(url)
.response.json()
для хранения данных ответа в объекте словаря. Обратите внимание, что это работает только потому, что результат записывается в формате JSON — иначе бы возникла ошибка.Код состояния, возвращенный нам API, мы можем проверить следующим образом:
# Print status code from original response (not JSON) print(response.status_code) # Результат """ 200 """
Результат показывает, что код состояния равен 200. Это значит, что запрос выполнен успешно.
В GET-запрос также можно передавать аргументы. Для этого нужно немного изменить приведенный выше код. Вот как теперь он будет выглядеть:
# Конечная точка API url = "https://jsonplaceholder.typicode.com/posts/" # Добавляем переменную payload payload = {"id": [1, 2, 3], "userId":1} # GET-запрос к API response = requests.get(url, params=payload) # Выводим ответ в консоль response_json = response.json() for i in response_json: print(i, "\n") # Результат """ {'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'} {'userId': 1, 'id': 2, 'title': 'qui est esse', 'body': 'est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla'} {'userId': 1, 'id': 3, 'title': 'ea molestias quasi exercitationem repellat qui ipsa sit aut', 'body': 'et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut'} """
Вот что мы изменили:
1
.payload
в аргумент param
метода request.get()
.for
и вывели каждый его элемент в новой строке.GET-запросы позволяют вам получать данные. HTTP-запросы POST позволяют создавать новые данные. Давайте посмотрим, как мы можем создавать новые данные на сервере JSONPlaceholder.
# Определяем новые данные new_data = { "userID": 1, "id": 1, "title": "Making a POST request", "body": "This is the data we created." } # Конечная точка API для связи url_post = "https://jsonplaceholder.typicode.com/posts" # POST-запрос к API post_response = requests.post(url_post, json=new_data) # Выводим ответ в консоль post_response_json = post_response.json() print(post_response_json) # Результат """ {'userID': 1, 'id': 101, 'title': 'Making a POST request', 'body': 'This is the data we created.'} """
В приведенном выше коде мы сделали следующее:
request.post()
. Обратите внимание, что в методе post()
мы установили параметр json
. Мы сделали это, чтобы в явном виде сообщить API, что мы отправляем данные в формате JSON.response.json()
для сохранения данных ответа в объекте словаря.Но минуточку!
Прежде чем вы прочитаете следующий фрагмент кода, потратьте 20 секунд на то, чтобы подумать о том, какой код состояния мы можем ожидать от API.
Имейте ввиду, что на этот раз мы создали новые данные, а не просто их извлекли.
Ок, вот код ответа от сервера:
# Print status code from original response (not JSON) print(post_response.status_code) # Результат: """ 201 """
В этом случае код состояния равен 201. Это указывает на то, что запрос был выполнен успешно и на сервере был создан новый ресурс.
А вы угадали ответ?
До этого момента взаимодействие с REST API было довольно простым. API сервера JSONPlaceholder не требует никакой аутентификации, чтобы вы могли начать с ним взаимодействовать. Но зачастую бывает, что REST API требует аутентификацию, прежде чем будет предоставлен доступ к определенным конечным точкам. Особенно это касается случаев, когда вы имеете дело с конфиденциальными данными.
Например, если вы хотите создавать интеграции, извлекать данные и автоматизировать свои рабочие процессы на GitHub, вы можете сделать это с помощью GitHub REST API. Однако в GitHub REST API есть много операций, требующих аутентификации, например получение общедоступной и частной информации о аутентифицированных пользователях.
Вот простой способ сделать это с использованием модуля requests Python:
from requests.auth import HTTPBasicAuth private_url = "https://api.github.com/user" github_username = "username" token = "token" private_url_response = requests.get( url=private_url, auth=HTTPBasicAuth(github_username, token) ) private_url_response.status_code # Результат: """ 200 """
В данном коде мы сделали следующее:
HTTPBasicAuth
из request.auth
. Этот объект прикрепляет базовую аутентификацию HTTP к нашему объекту запроса — это по сути то же самое, что и ввод вашего имени пользователя и пароля на сайте.private_url
.token
с личным токеном доступа для аутентификации в Github.private_url_response
.Бывают случаи, когда запросы к API выполняются не так, как ожидалось. На это может влиять множество факторов, как на стороне клиента, так и на стороне сервера. Независимо от причины, результат всегда один и тот же: запрос не выполняется.
При использовании REST API всегда полезно сделать свой код устойчивым. Однако, прежде чем вы сможете писать надежный код, вы должны понять, как управлять сообщениями об ошибках, когда что-то идет не по плану.
Давайте опять вернемся к API сервера JSONPlaceholder. Мы начнем с написания кода, а затем объясним, что происходит.
# В конечной точке "postz" вместо "posts". Была сделана преднамеренная опечатка. url = "https://jsonplaceholder.typicode.com/postz" # Attempt to GET data from provided endpoint try: response = requests.get(url) response.raise_for_status() # If the request fails (404) then print the error. except requests.exceptions.HTTPError as error: print(error) # Результат: """ 404 Client Error: Not Found for url: https://jsonplaceholder.typicode.com/postz """
В данном коде мы сделали следующее:
try
и except
перехватывают любые ошибки, возникающие при попытке обратиться к серверу JSONPlaceholder. Обратите внимание, что метод raise_for_status()
используется для возврата объекта HTTPError при возникновении ошибки во время процесса запроса.Хотя в этом примере мы продемонстрировали, как обрабатывать ошибку 404, тот же формат можно использовать для обработки любого кода состояния HTTP.
Коды состояния HTTP в формате 3xx указывают на то, что клиент был переадресован и должен выполнить некоторые дополнительные действия для выполнения запроса. Но иногда это может приводить к ситуациям, когда получается бесконечный цикл переадресаций.
Для решения этой проблемы модуль requests предоставляет объект TooManyRedirects
, который работает следующим образом:
""" Примечание: данный код здесь не вызовет ошибку, но его структура такова, что в нем есть несколько переадресаций """ url = "https://jsonplaceholder.typicode.com/posts" try: response = requests.get(url) response.raise_for_status() except requests.exceptions.TooManyRedirects as error: print(error)
Если переадресаций слишком много, метод raise_for_status()
вызовет исключение TooManyRedirects
, которое перехватывается блоком исключения и будет выведено сообщение об ошибке.
Вы также можете установить максимальное количество переадресаций в качестве параметра вашего метода HTTP-запроса:
# Решение 2 url = "https://jsonplaceholder.typicode.com/posts" session = requests.Session() session.max_redirects = 3 response = session.get(url)
Другой вариант — полностью отключить переадресацию:
# Решение 3 url = "https://jsonplaceholder.typicode.com/posts" session = requests.Session() session.allow_redirects = False response = session.get(url)
Строка session.allow_redirects = False
устанавливает для атрибута allow_redirects
объекта Session
значение False
, что означает, что сеанс не будет следовать никаким переадресациям, которые могут быть возвращены сервером.
Это еще один тип ошибок, с которыми вы можете столкнуться при отправке запросов на сервер. Существует несколько причин, по которым вы можете не получить ответ от сервера. Например, сбой DNS, отказ в подключении, проблемы с подключением к Интернету и так далее. Но результат одинаков: возникает ошибка подключения.
Вы можете использовать объект ConnectionError
модуля requests, чтобы обнаружить такого рода проблемы и обработать их соответствующим образом.
Вот как будет выглядеть код:
""" Примечание: данный код здесь не вызовет ошибку, но его структура такова, что если произойдет ошибка подключения, то она будет обработана. """ url = "https://jsonplaceholder.typicode.com/posts" try: response = requests.get(url) except requests.ConnectionError as error: print(error)
Когда API сервера принимает соединение, но не может выполнить ваш запрос в отведенное время, вы получите так называемую timeout error
(ошибку таймаута).
Мы продемонстрируем, как справиться с этим случаем, установив для параметра timeout
в методе request.get()
очень маленькое число. Это вызовет ошибку, и мы ее обработаем с помощью объекта request.Timeout
.
url = "https://jsonplaceholder.typicode.com/posts" try: response = requests.get(url, timeout=0.0001) except requests.Timeout as error: print(error)
Самый простой путь для обхода ошибок таймаута — установить большее число в параметр timeout
. Другие решения могут включать в себя оптимизацию ваших запросов, включение цикла повторных попыток в ваши сценарии или выполнение асинхронных вызовов API.
Последний метод позволяет вашему программному обеспечению начать потенциально длительную деятельность, реагируя на другие события, а не ждать, пока первоначальная задача будет выполнена.
В данной статье мы рассмотрели, что такое API, и изучили общую архитектуру API, называемую REST. Мы также рассмотрели HTTP-запросы и то, как мы можем использовать библиотеку requests языка Python для взаимодействия с веб-сервисами.
Перевод статьи Web APIs, Python Requests & Performing an HTTP Request in Python Tutorial.
Управление памятью - важный, но часто упускаемый из виду аспект программирования. При неправильном подходе оно…
Как возникает круговой импорт? Эта ошибка импорта обычно возникает, когда два или более модуля, зависящих…
Вы когда-нибудь оказывались в ситуации, когда скрипт на Python выполняется очень долго и вы задаетесь…
В этом руководстве мы разберем все, что нужно знать о символах перехода на новую строку…
Блок if __name__ == "__main__" в Python позволяет определить код, который будет выполняться только при…
Давайте разберем, как настроить модульные тесты для экземпляров классов. Мы напишем тесты для проверки функциональности…