В этой статье мы рассмотрим общий принцип взаимодействия с веб-API, используя Python, на примере Timeweb Cloud. Научимся получать информацию об аккаунте и добавлять SSH-ключи.
API — сокращение от Application Programming Interface (программный интерфейс приложения). Благодаря ему можно получить информацию от других программ / сервисов или автоматизировать рутинные действия, выполняемые в браузере.
Python — отличный выбор для работы с API по нескольким причинам:
Перед началом работы с любым API необходимо в первую очередь ознакомиться с его документацией. Это позволит узнать, какие функции и методы доступны, каким образом можно отправлять запросы (описание конечных точек), и какие коды ответов могут быть получены от API.
Обратившись к документации веб-API Timeweb Cloud, мы увидим, что все запросы выполняются с использованием HTTP-протокола и все ответы возвращаются в виде JSON, поддерживаются методы GET, POST, PUT, DELETE и PATCH.
Также в документации описаны возможные коды ответов. Коды ответов можно разделить на 3 группы:
Отдельно стоит отметить 429 код ответа — он может возникнуть, если было выполнено более 20 запросов в секунду.
Стоит упомянуть, что как и у многих других сервисов, у Timeweb Cloud есть «оболочка» над веб-API — Timeweb Cloud CLI. Этот инструмент позволяет создавать и настраивать облачные серверы, хранилища данных и прочие облачных ресурсы средствами командной строки.
Приступим к созданию проекта. Создайте директорию TWAPI
:
mkdir TWAPI
Перейдите в новый каталог:
cd ./TWAPI
Создайте виртуальное окружение Python:
python3 -m venv venv
Виртуальное окружение в Python используется для изоляции проектов и их зависимостей, обеспечивая чистоту кода и избегая конфликтов между библиотеками разных версий.
Активируйте виртуальное окружение:
source ./venv/bin/activate
Установим библиотеку requests
:
pip install requests
Перед началом работы с веб-API, нам необходимо получить токен. Токен — это специальный ключ, который используется для аутентификации и авторизации пользователя при доступе к ресурсам. Он обеспечивает безопасность, контроль доступа и идентификацию запросов, позволяя серверу определить, какие действия и данные может выполнять или получать конкретный пользователь или приложение.
Для создания токена переходим во вкладку «API и Terrafrom» в панели управления аккаунтом.
Нажимаем «Создать». Вводим имя токена, при необходимости меняем срок действия токена. Галочку с «Удалять сервисы по API без кода в Телеграме» можно не снимать.
После нажатия на кнопку «Выпустить» появится возможность скопировать токен. На данном этапе его стоит сохранить в каком-либо файле, так как после закрытия окна увидеть токен вновь не удастся. Временно сохранить токен можно в файл token.txt
, выполнив команду в директории проекта:
echo 'скопированный_токен' > token.txt
ВАЖНО: Если стороннее лицо завладеет вашим токеном, ему будут доступны любые операции с вашим аккаунтом. Поэтому, если вы собираетесь загружать проект, например, в публичный репозиторий github, файл
token.txt
необходимо добавить в.gitignore
.
Выполнив настройку среды и получив токен для работы с веб-API, создадим файл main.py
в директории проекта. В файле выполним подключение библиотек requests
и json
:
import requests
import json
Эти инструкции подключают библиотеки, облегчающие выполнение HTTP-запросов к серверу и работу с форматом JSON.
Следующим шагом объявим необходимые переменные:
token = 'токен_полученный_ранее'
api_url_base = 'https://api.timeweb.cloud/api/v1/'
ВАЖНО: В «боевых» проектах не стоит указывать токен в коде скрипта. Чувствительные данные не должны храниться в коде скрипта, так как это создает риск утечки данных в случае доступа к исходному коду или его случайной публикации. Для подключения токена лучше использовать переменные окружения. В рамках этой статьи токен будет указан в коде скрипта для упрощения демонстрации.
Переменная api_url_base
хранит в себе URl, одинаковый для всех конечных точек API.
Далее настроим HTTP-заголовки, следуя документации:
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
Заголовок Content-Type
указывает серверу на то, что в теле запроса передаются данные в формате JSON. Заголовок Authorization
должен содержать в себе токен. Его можно было бы указать строкой в заголовке, но тогда код становится нечитаемым и неподдерживаемым.
Полный листинг main.py
:
import requests
import json
token = 'токен_полученный_ранее'
api_url_base = 'https://api.timeweb.cloud/api/v1/'
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
На этом подготовительный этап завершен, и мы можем приступить к выполнению запросов.
Обратимся к документации и увидим, что для получения платежной информации необходимо отправить GET-запрос к https://api.timeweb.cloud/api/v1/account/finances
. Из документации также узнаем, что при корректном запросе код ответа будет 200.
Реализуем функцию для получения платежной информации аккаунта:
def get_finances_info():
api_url = f'{api_url_base}account/finances'
response = requests.get(api_url, headers=headers)
if response.status_code == 200:
print('Платежная информация:')
for key, value in json.loads(response.content.decode('utf-8'))['finances'].items():
print(key, value)
else:
print (f'Ошибка {response.status_code}')
Распишем подробнее:
api_url
при помощи f-строк мы объединили api_url_base
, объявленную ранее, с account/finances
. В результате мы получили путь к конечной точке API.response
мы сохраняем результат выполнения GET-запроса. При выполнении запроса также были переданы заголовки, сохраненные в переменной headers
.if response.status_code == 200:
проверяем код ответа, и если он равен 200 (код состояния, который мы должны получить при успешном обращении), выводим строку «Платежная информация»:
.json.loads(response.content.decode('utf-8'))['finances']
декодирует JSON-ответ от сервера (response.content
) из байтового формата в текстовый формат с кодировкой UTF-8, а затем извлекает словарь с ключом 'finances'
из этого JSON-ответа.for key, value in ...
перебираются элементы этого словаря и выводятся на экран ключи и соответствующие им значения.else
, и на экран выводится сообщение об ошибке, содержащее код состояния ответа.Вызовем нашу функцию, добавив в конце main.py
get_finances_info()
Итоговый листинг main.py
:
import requests
import json
token = 'токен_полученный_ранее'
api_url_base = 'https://api.timeweb.cloud/api/v1/'
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
def get_finances_info():
api_url = f'{api_url_base}account/finances'
response = requests.get(api_url, headers=headers)
if response.status_code == 200:
print('Платежная информация:')
for key, value in json.loads(response.content.decode('utf-8'))['finances'].items():
print(key, value)
else:
print (f'Ошибка {response.status_code}')
get_finances_info()
Запустим наш код:
python3 main.py
В результате выполнения скрипта в терминале получим вывод:
Платежная информация:
balance 149.49
currency RUB
discount_end_date_at None
discount_percent 0
hourly_cost 0
hourly_fee 0
monthly_cost 0
monthly_fee 0
total_paid 767
hours_left None
autopay_card_info None
В следующем примере опишем добавление SSH-ключа в панель управления при помощи веб-API. Вновь обращаемся к документации.
Видим, что добавление выполняется при помощи POST-запроса к конечной точке https://api.timeweb.cloud/api/v1/ssh-keys
. В теле запроса необходимо передать сам ключ, имя и параметр is_default
. При успешном добавлении возвращается код состояния 201.
Приступим к написанию функции:
def add_ssh_key():
api_url = f'{api_url_base}ssh-keys'
ssh_key_path = '/home/user/.ssh/id_rsa.pub'
try:
with open(ssh_key_path, 'r') as file:
ssh_key = file.read()
data = {
'body': ssh_key,
'is_default': False,
'name': 'My SSH key'
}
json_data = json.dumps(data)
response = requests.post(api_url, headers=headers, data=json_data)
if response.status_code == 201:
print('Ключ успешно добавлен! \nДанные ключа:')
for key, value in json.loads(response.content.decode('utf-8'))['ssh_key'].items():
print(key, value)
else:
print(f'Возникла ошибка {response.status_code}')
except FileNotFoundError:
print(f'Файл {ssh_key_path} не найден.')
except Exception as e:
print(f'Произошла ошибка: {str(e)}')
add_ssh_key()
Здесь:
ssh_key_path
указываем путь к SSH-ключу (если у вас нет ключа, сгенерировать его можно командой ssh-keygen -t rsa
). try
выполняются следующие действия:
ssh_key
;is_default
и имя ключа;json.dumps()
;except
обрабатываются исключения, такие как FileNotFoundError
(если файл с ключом не найден) и общие исключения Exception
, выводя соответствующие сообщения об ошибках.add_ssh_key()
.Полный листинг main.py
(код из этого раздела не связан с функцией, созданной в предыщем разделе, поэтому в листинге не указан. Вызов get_finances_info()
из прошлой главы можно закомментировать, добавив #
перед вызовом):
import requests
import json
token = 'токен_полученный_ранее'
api_url_base = 'https://api.timeweb.cloud/api/v1/'
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
def add_ssh_key():
api_url = f'{api_url_base}ssh-keys'
ssh_key_path = '/home/user/.ssh/id_rsa.pub'
try:
with open(ssh_key_path, 'r') as file:
ssh_key = file.read()
data = {
'body': ssh_key,
'is_default': False,
'name': 'My SSH key'
}
json_data = json.dumps(data)
response = requests.post(api_url, headers=headers, data=json_data)
if response.status_code == 201:
print('Ключ успешно добавлен! \nДанные ключа:')
for key, value in json.loads(response.content.decode('utf-8'))['ssh_key'].items():
print(key, value)
else:
print(f'Возникла ошибка {response.status_code}')
except FileNotFoundError:
print(f'Файл {ssh_key_path} не найден.')
except Exception as e:
print(f'Произошла ошибка: {str(e)}')
add_ssh_key()
Запускаем скрипт командой python3 main.py
. В терминале видим, что ключ успешно добавлен, также получаем информацию о добавленном ключе:
Ключ успешно добавлен!
Данные ключа:
id 111235
body ssh-rsa "тут указан ключ"
created_at 2023-09-21T00:45:00.000Z
expired_at None
is_default False
name My SSH key
used_by []
В панели управления переходим в раздел «SSH-ключи» и видим, что ключ действительно добавлен:
Рассмотрим еще один метод API, который использует PATCH для изменения имени SSH-ключа. В этом примере нам понадобится id
добавленного ранее ключа, который мы получили в результате выполнения прошлой функции.
def change_ssh_key_name(key_id):
api_url = f'{api_url_base}ssh-keys/{key_id}'
data = {
'name': 'Changed Name'
}
json_data = json.dumps(data)
response = requests.patch(api_url, headers=headers, data=json_data)
if response.status_code == 200:
print('Имя успешно изменено! \nДанные ключа:')
for key, value in json.loads(response.content.decode('utf-8'))['ssh_key'].items():
print(key, value)
else:
print(f'Возникла ошибка {response.status_code}')
change_ssh_key_name(111235)
В этом примере мы создаем функцию change_ssh_key_name
, принимающую параметр key_id
, значение которого используется при формировании api_url
. Метод PATCH подразумевает частичное обновление информации, поэтому в словаре data
мы не указываем все параметры, что использовались при добавлении ключа, а только те, что хотим изменить.
При запуске скрипта мы получим подобный вывод:
Имя успешно изменено!
Данные ключа:
id 111235
body ssh-rsa "тут указан ключ"
created_at 2023-09-21T02:08:39.000Z
expired_at None
is_default False
name Changed Name
used_by []
Откроем раздел «SSH-ключи» и убедимся, что имя действительно изменено:
В этом руководстве на примере взаимодействия с веб-API Timeweb Cloud мы рассмотрели, как при помощи Python можно работать с API. Используя примеры из статьи, вам не составит труда переписать функции для выполнения запросов к другим конечным точкам, описанных в документации. Также мы описали общий алгоритм, благодаря которому работа с API Timeweb Cloud или других сервисов не вызовет у вас проблем.