Давайте дружить в Телеграме: рассказываем про новые фичи, общаемся в комментах, прислушиваемся к вашим идеям Подписаться

Как использовать веб-API в Python 3

Никита Долгих
Никита Долгих
Технический писатель
04 октября 2023 г.
421
13 минут чтения
Средний рейтинг статьи: 5

В этой статье мы рассмотрим общий принцип взаимодействия с веб-API, используя Python, на примере Timeweb Cloud. Научимся получать информацию об аккаунте и добавлять SSH-ключи.

API — сокращение от Application Programming Interface (программный интерфейс приложения). Благодаря ему можно получить информацию от других программ / сервисов или автоматизировать рутинные действия, выполняемые в браузере.

Python — отличный выбор для работы с API по нескольким причинам:

  • Во-первых, Python имеет большое количество библиотек и модулей, которые упрощают взаимодействие с API. Это позволяет легко выполнять HTTP-запросы, обрабатывать JSON и другие форматы данных.
  • Во-вторых, Python известен своей простотой и читаемостью кода. 
  • В-третьих, Python популярен среди разработчиков, что означает наличие обширного сообщества и множества ресурсов для обучения и поддержки.
  • И наконец, Python мультиплатформен, что означает, что вы можете разрабатывать и запускать приложения, использующие API, на разных операционных системах без больших изменений в коде.

Знакомство с веб-API Timeweb Cloud

Перед началом работы с любым API необходимо в первую очередь ознакомиться с его документацией. Это позволит узнать, какие функции и методы доступны, каким образом можно отправлять запросы (описание конечных точек), и какие коды ответов могут быть получены от API.

Обратившись к документации веб-API Timeweb Cloud, мы увидим, что все запросы выполняются с использованием HTTP-протокола и все ответы возвращаются в виде JSON, поддерживаются методы GET, POST, PUT, DELETE и PATCH. 

Также в документации описаны возможные коды ответов. Коды ответов можно разделить на 3 группы:

  • 2** — означает «успех». Запрос был выполнен корректно, в теле ответа содержится информация, если это было предусмотрено.
  • 4** - означает, что в запросе допущена ошибка. Ошибка не связана с работой сервера, а возникает именно из-за некорректно выполненного запроса.
  • 5** — ошибка сервера. Ошибка может возникнуть, если на сервере возникли какие-то проблемы. При возникновении ошибки стоит подождать некоторое время. Если ошибка не пропадает, стоит обратиться в поддержку с подробным описанием того, как воспроизвести проблему.

Отдельно стоит отметить 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» в панели управления аккаунтом.

Image4

Нажимаем «Создать». Вводим имя токена, при необходимости меняем срок действия токена. Галочку с «Удалять сервисы по API без кода в Телеграме» можно не снимать.

60205da3 A4a7 40ba Baa7 57e54e0be86f

После нажатия на кнопку «Выпустить» появится возможность скопировать токен. На данном этапе его стоит сохранить в каком-либо файле, так как после закрытия окна увидеть токен вновь не удастся. Временно сохранить токен можно в файл token.txt, выполнив команду в директории проекта:

echo 'скопированный_токен' > token.txt

Image5

ВАЖНО: Если стороннее лицо завладеет вашим токеном, ему будут доступны любые операции с вашим аккаунтом. Поэтому, если вы собираетесь загружать проект, например, в публичный репозиторий 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 ... перебираются элементы этого словаря и выводятся на экран ключи и соответствующие им значения.
  • В случае, если код состояния ответа не равен 200, выполняется блок 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-ключа в панель управления аккаунтом

В следующем примере опишем добавление 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-ключа в режиме чтения;
    • Считывается содержимое файла в переменную ssh_key;
    • Создается словарь data, который содержит сам ключ, флаг is_default и имя ключа;
    • Данные преобразуются в JSON-формат с помощью json.dumps();
    • Выполняется HTTP POST-запрос к API с указанием заголовков и JSON-данных;
    • Если ответ имеет статус код 201 (Created), то выводится сообщение об успешном добавлении и данные о ключе;
    • В противном случае выводится сообщение об ошибке с указанием статус кода;
  • В блоке 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-ключи» и видим, что ключ действительно добавлен:

Image2

Изменение имени 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-ключи» и убедимся, что имя действительно изменено:

Image3

Заключение

В этом руководстве на примере взаимодействия с веб-API Timeweb Cloud мы рассмотрели, как при помощи Python можно работать с API. Используя примеры из статьи, вам не составит труда переписать функции для выполнения запросов к другим конечным точкам, описанных в документации. Также мы описали общий алгоритм, благодаря которому работа с API Timeweb Cloud или других сервисов не вызовет у вас проблем.

  1. Исследование документации:
    • Первым шагом является поиск документации по API нужного сервиса. Это может быть официальная документация на веб-сайте провайдера или другие ресурсы, предоставляющие информацию о взаимодействии с API.
    • Ознакомьтесь с основами взаимодействия, включая формат запросов и ответов, доступные методы и ожидаемые данные.
  2. Аутентификация:
    • Если для работы с API требуется аутентификация, получите необходимые учетные данные или токен. 
  3. Создание запросов:
    • Используя данные из документации, создайте HTTP-запросы.
    • Укажите необходимые параметры запроса: метод (GET, POST, PUT, DELETE, PATCH), заголовки, тело запроса (если необходимо)
  4. Обработка ошибок:
    • Предусмотрите обработку ошибок. Это может включать в себя проверку статус-кодов ответов (например, 200 - успешно, 4xx - клиентская ошибка, 5xx - серверная ошибка) и обработку исключений.

Зарегистрируйтесь и начните пользоваться
сервисами Timeweb Cloud прямо сейчас

15 лет опыта
Сосредоточьтесь на своей работе: об остальном позаботимся мы
165 000 клиентов
Нам доверяют частные лица и компании, от небольших фирм до корпораций
Поддержка 24/7
100+ специалистов поддержки, готовых помочь в чате, тикете и по телефону