HTTP
Давайте подумаем о том, каким образом данные передаются от одного устройства другому в компьютерных сетях. Данные изначально представлены в виде последовательности байтов. А между устройствами эти байты уже передаются в виде физического сигнала различной природы: электричество, радиоволна, свет и др. Всего выделяют семь уровней, которые проходят данные, начиная с представления в виде байтов и заканчивая физическим сигналом.
Эти уровни передачи определены в стандарте OSI (The Open Systems Interconnection model, модель взаимодействия открытых систем). На каждом уровне описаны протоколы, по которым происходит обмен данными. Протокол — это набор правил, который определяет процесс обмена данными между различными устройствами или программами.
Программы работают на самом высоком уровне — прикладном. Программисты для передачи данных между программами выбирают один из существующих протоколов прикладного уровня и следуют его правилам. Такой подход позволяет не задумываться о том, как передавать данные на более низких уровнях. Это упрощает и ускоряет разработку сетевых программ.
Для обмена данными между программами в компьютерных сетях используются различные протоколы прикладного уровня. Один из самых популярных протоколов — HTTP (HyperText Transfer Protocol, протокол передачи гипертекста). Он создавался для передачи гипертекстовых документов формата HTML (веб-страницы), однако в настоящее время используется для обмена произвольными данными: графическими и видеофайлами, документами и т. д.
Протокол HTTP работает по принципу «запрос — ответ». В протоколе описаны виды запросов, правила формирования запросов и возможные варианты ответов на запросы. Ответы в протоколе HTTP имеют коды состояния, которые представляют собой числовые значения. По коду состояния всегда можно определить, верно ли был обработан запрос или произошла ошибка. Например, вам, скорее всего, знаком код состояния 404
, который означает, что запрошенный объект (чаще всего веб-страница) не был найден.
В протоколе HTTP описаны различные виды запросов: на получение данных (GET), на передачу данных (POST), на добавление и изменение данных (PUT), на удаление данных (DELETE) и др.
Итак, многие сетевые программы взаимодействуют по протоколу HTTP. Часто разработчикам веб-сервисов нужно отправлять в качестве ответа на запросы не веб-страницы, а данные, например информацию о географических объектах в формате JSON или графический файл с определённой областью географической карты. Порой это помогает расширить функционал сервиса. К примеру, добавить авторизацию через соцсети.
Бывает и коммерческий вариант такого взаимодействия, когда сторонние разработчики платят за то, чтобы использовать возможность работы с сервисами для создания собственных продуктов.
Реализация взаимодействия с сервисом через такой набор правил происходит посредством API (Application Programming Interface). Правила API описывают возможные запросы к сервису и ответы сервиса на эти запросы. Так, API отдельного веб-сервиса можно назвать нестандартным протоколом этого сервиса, действующим над протоколом HTTP.
Для работы с API можно не писать программу, а просто выполнить запрос в браузере, так как он также работает по протоколу HTTP.
Static API
Воспользуемся в качестве примера одним из API сервиса Яндекс Карт — Static API. Static API возвращает изображение карты в ответ на HTTPS-запрос. Добавляя в URL разные параметры и задавая их значения, вы можете определить центр карты, её размер и область показа, отметить нужные объекты и отобразить пробки. Данные будут обновляться при каждом новом обращении, поэтому карта всегда будет актуальной.
Обратите внимание: у каждого API есть условия использования, которые нужно обязательно изучить перед началом работы и которым нужно следовать при разработке программ на основе API. Например, для Static API в бесплатной версии API Яндекс Карт изображение карты обязательно должно быть размещено на общедоступном сайте или в приложении. Есть и другие условия бесплатного использования.
Давайте сделаем первый запрос к Static API. Для этого надо открыть документацию и понять, каким должен быть запрос. Запрос к Static API имеет следующий формат: https://static-maps.yandex.ru/1.x/?. Воспользуемся примерами использования и выполним в браузере следующий запрос:
https://static-maps.yandex.ru/1.x/?ll=37.677751,55.757718&spn=0.016457,0.00619&l=map
Параметр ll
отвечает за координаты центра карты (через запятую указываются долгота и широта в градусах). Параметр spn
определяет область показа (протяжённость карты в градусах по долготе и широте). Параметр l
определяет тип карты (в запросе используется тип map
— схема). Все возможные параметры запроса можно посмотреть в документации.
В ответе на запрос сервер пришлёт часть карты по запрошенным координатам.
Чтобы воспользоваться API в программе, нужно из неё отправить такой же запрос, а затем получить ответ сервера. Для удобного формирования HTTP-запросов и получения ответов можно использовать библиотеку requests
.
Библиотека requests
является нестандартной и устанавливается следующей командой:
pip install requests
В библиотеке requests
функции для формирования HTTP-запросов называются так же, как и сами запросы. Например, для выполнения GET-запроса используем функцию get()
:
from requests import get
response = get("https://static-maps.yandex.ru/1.x/?"
"ll=37.677751,55.757718&"
"spn=0.016457,0.00619&"
"l=map")
print(response)
Вывод программы:
<Response [200]>
Функция get()
вернула ответ HTTP-сервера с кодом 200. Значит, запрос был обработан успешно. Для получения данных из ответа сервера воспользуемся атрибутом content
. В этом атрибуте находятся данные в виде последовательности байтов. Из документации Static API известно, что ответом сервера на успешный запрос должен быть графический файл формата PNG
. Запишем данные из ответа сервера в новый файл с расширением .png
. Для этого откроем файл функцией open()
на запись в бинарном режиме (wb
), так как будем сохранять байты, а не текст. А затем воспользуемся методом write()
созданного файлового объекта:
with open("map.png", "wb") as file:
file.write(response.content)
В папке с программой будет создан файл map.png
с запрошенной областью карты.
В нашей программе запрос формируется в виде длинной строки, включающей в себя адрес сервера и параметры запроса. Функция get()
имеет аргумент params
, в который можно передать словарь с параметрами запроса:
params = {"ll": "37.677751,55.757718",
"spn": "0.016457,0.00619",
"l": "map"}
response = get("https://static-maps.yandex.ru/1.x/", params=params)
При выполнении запроса к серверу могут происходить нештатные ситуации. Например, отсутствие подключения к сети. В таком случае программа вызовет исключение ConnectionError
из модуля requests
. Обработка исключения поможет нашей программе не падать с ошибкой, а сообщить о необходимости проверки подключения к сети:
from requests import get, ConnectionError
params = {"ll": "37.677751,55.757718",
"spn": "0.016457,0.00619",
"l": "map"}
try:
response = get("https://static-maps.yandex.ru/1.x/", params=params)
except ConnectionError:
print("Проверьте подключение к сети.")
else:
with open("map.png", "wb") as file:
file.write(response.content)
Static API имеет очень широкий функционал, поэтому рассмотреть его весь в данном параграфе невозможно. Для более детального изучения API воспользуйтесь документацией.
API Яндекс Диск
Рассмотрим ещё один пример взаимодействия с API. Яндекс Диск — это облачный сервис для хранения файлов и обмена ими. Он предоставляет API для сохранения файлов и организации доступа к ним. Полный список запросов к API и формат ответов сервера находится в документации.
Для работы с API Яндекс Диска требуется авторизация пользователя по протоколу OAuth 2.0. Открытый протокол авторизации OAuth 2.0 обеспечивает предоставление третьей стороне ограниченного доступа к защищённым ресурсам пользователя без передачи ей логина и пароля. Вместо логина и пароля авторизация производится по OAuth-токену. Токен — это уникальная для каждого пользователя строка-ключ для доступа с помощью API к файлам в облаке.
Для работы с API необходимо создать и зарегистрировать приложение-сервис по инструкции. При создании сервиса выполните следующие действия:
- Заполнить поле «Название сервиса».
- Выбрать вариант платформы «Веб-сервисы».
- Выбрать значение «Подставить URL для разработки» в поле Callback URL.
- В качестве доступных сервису данных указать «Яндекс Диск REST API» и выдать все четыре возможных права доступа.
- Нажать кнопку «Создать приложение».
После выполнения указанных действий появится окно, в котором будут указаны значения для полей ClientID
, Client secret
и Redirect URL
. Эти значения мы будем использовать для получения токена по протоколу авторизации OAuth 2.0. Данный протокол реализован в библиотеке requests-oauthlib
.
Приложение-сервис должно перенаправить пользователя по ссылке для авторизации. Пользователь переходит по ссылке и копирует сгенерированный одноразовый код, а затем вводит его в приложение. Библиотека requests-oauthlib
на основе введённого кода делает запрос на получение токена и возвращает его. Более детальный порядок получения токена изложен в инструкции.
Напишем программу с авторизацией сервиса и получением токена. Значения переменных client_id
и client_secret
нужно скопировать из полей ClientID
, Client secret
со страницы информации о зарегистрированном сервисе.
from requests_oauthlib import OAuth2Session
from requests import get, post, put, delete
client_id = ""
client_secret = ""
auth_url = "https://oauth.yandex.ru/authorize"
token_url = "https://oauth.yandex.ru/token"
oauth = OAuth2Session(client_id=client_id)
authorization_url, state = oauth.authorization_url(auth_url, force_confirm="true")
print("Перейдите по ссылке, авторизуйтесь и скопируйте код:", authorization_url)
code = input("Вставьте одноразовый код: ")
token = oauth.fetch_token(token_url=token_url,
code=code,
client_secret=client_secret)
access_token = token["access_token"]
print(access_token)
Переменная token
является словарём, в котором необходимый для выполнения запросов токен находится по ключу access_token
.
Полученный на этапе авторизации токен необходимо передавать во всех запросах к API. Для этого нужно заполнять в заголовке запроса поле Authorization
значением OAuth <ваш токен>
.
Создадим словарь headers
с соответствующими ключом и значением. Заголовок с токеном будут передаваться в функции запросов модуля requests
через именованный аргумент с таким же именем headers
.
Все возможные запросы к сервису, их формат и ответы сервера можно посмотреть в документации.
Выполним GET-запрос по адресу https://cloud-api.yandex.net/v1/disk для получения информации о состоянии облачного хранилища:
headers = {"Authorization": f"OAuth {access_token}"}
r = get("https://cloud-api.yandex.net/v1/disk", headers=headers)
print(r.json())
Данные в ответе на запрос имеют формат JSON. Они преобразуются в словарь методом json()
. В ответе содержится информация о максимальном размере файла на диске (поле max_file_size
), о размере диска (поле total_space
), об использованном объёме (used_space
) и др. Полное описание полей ответа можно посмотреть в документации.
Создадим в корневой папке диска новую папку Тест API
. Скопируем файл map.png
, полученный в примерах для Static API, в эту папку. Для создания папки потребуется выполнить PUT-запрос по адресу https://cloud-api.yandex.net/v1/disk/resources с обязательным параметром path
, в котором должен быть записан путь к создаваемой папке.
params = {"path": "Тест API"}
r = put("https://cloud-api.yandex.net/v1/disk/resources", headers=headers, params=params)
print(r)
В случае успешного запроса программа выведет код ответа <Response [201]>, а в корневой папке облачного диска будет создана папка Тест API
:
Для копирования файла в папку необходимо выполнить два запроса:
- GET-запрос по адресу https://cloud-api.yandex.net/v1/disk/resources/upload для получения ссылки на загрузку файла в облако. Если запрос будет успешен, то URL для загрузки файла будет в поле
href
JSON-ответа сервера. - PUT-запрос по URL из предыдущего пункта. В аргумент
files
функцииput
передаётся словарь с ключомfile
и значением — файловым объектом, открытым в режиме бинарного чтенияrb
. Передавать токен в данном запросе не нужно.
Выполним указанные запросы в программе:
params = {"path": "Тест API/map.png"}
r = get("https://cloud-api.yandex.net/v1/disk/resources/upload",
headers=headers, params=params)
href = r.json()["href"]
files = {"file": open("map.png", "rb")}
r = put(href, files=files)
print(r)
В результате успешного запроса программа выведет <Response [201]>, а в папке Тест API
в облаке появится файл map.png
:
Попробуйте реализовать другие операции с хранилищем через запросы к API в соответствии с документацией.
Кроме рассмотренных API сервисов Яндекса существует большое количество других сервисов. Теперь вы знаете, как ваше приложение должно с ними взаимодействовать для обмена данными.