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

Вступление

API, или snterfaceApplicationProgramI, позволяет разработчикам интегрировать одно приложение с другим. Они раскрывают некоторые внутренние механизмы программы ограниченным образом.

Вы можете использовать API для получения информации из других программ или для автоматизации вещей, которые вы обычно делаете в своем веб-браузере. Иногда вы можете использовать API, чтобы делать то, что вы просто не можете делать другим способом. Удивительное количество веб-ресурсов предлагают веб-API вместе с более привычным веб-сайтом или мобильным приложением, включая Twitter, Facebook, GitHub и DigitalOcean.

Если вы прошли через несколько руководств поhow to code in Python 3 и вам удобны синтаксис, структура и некоторые встроенныеfunctions Python, вы можете писать программы Python, которые используют ваши любимые API.

В этом руководстве вы узнаете, как использовать Python сDigitalOcean API для получения информации о вашей учетной записи DigitalOcean. Затем мы рассмотрим, как применить полученные знания кGitHub’s API.

Когда вы закончите, вы поймете концепции, общие для всех веб-API, и у вас будет пошаговый процесс и рабочие примеры кода, которые вы можете использовать для опробования API-интерфейсов других служб.

Предпосылки

Прежде чем начать это руководство, вам потребуется следующее:

  • Локальная среда разработки для Python 3. Вы можете следоватьHow To Install and Set Up a Local Programming Environment for Python 3, чтобы настроить все, что вам нужно.

  • Текстовый редактор, который вам удобно использовать. Если у вас еще нет избранного, выберите его с подсветкой синтаксиса. Notepad++ для Windows,BBEdit для macOS иSublime Text илиAtom для любой платформы - все это хороший выбор.

  • Учетная запись DigitalOcean и ключ API. Первые несколько абзацев вHow To Use the DigitalOcean API v2 показывают, как это сделать.

[[step-1 -—- Get-known-an-api]] == Шаг 1. Знакомство с API

Первый шаг в использовании нового API - это найти документацию и понять, как это сделать. Документация по API DigitalOcean начинается сhttps://developers.digitalocean.com/. Чтобы найти API для других сервисов, выполните поиск по названию сайта и «API» - не все сервисы продвигают их на своих первых страницах.

У некоторых сервисов естьAPI wrappers. Оболочка API - это код, который вы устанавливаете в своей системе, чтобы упростить использование API на выбранном вами языке программирования. Это руководство не использует никаких оболочек, потому что они скрывают большую часть внутренней работы API и часто не раскрывают все, что может сделать API. Обертки могут быть хороши, когда вы хотите что-то сделать быстро, но твердое понимание того, что могут делать сами API, поможет вам решить, имеют ли обертки смысл для ваших целей.

Во-первых, взгляните на Введение в DigitalOcean API вhttps://developers.digitalocean.com/documentation/v2/ и попытайтесь понять только основы того, как отправлять запрос и чего ожидать в ответе. На данный момент вы пытаетесь изучить только три вещи:

  1. Как выглядит запрос? Они все только URL? Для более подробных запросов, как данные отформатированы? Обычно этоJSON или параметры строки запроса, как в веб-браузере, но некоторые используют XML или пользовательский формат.

  2. Как выглядит ответ? Документы API будут показывать образцы запросов и ответов. Собираетесь ли вы получить JSON, XML или какой-то другой ответ?

  3. Что входит в заголовки запроса или ответа? Часто заголовки запроса включают ваш токен аутентификации, а заголовки ответа предоставляют текущую информацию об использовании вами службы, например, насколько вы близки к пределу скорости.

API DigitalOcean использует HTTPmethods (иногда называемыйverbs), чтобы указать, пытаетесь ли вы прочитать существующую информацию, создать новую информацию или что-то удалить. В этой части документации объясняется, какие методы используются и для каких целей. Как правило, запрос GET проще, чем POST, но к тому времени, как вы это сделаете, вы не заметите большой разницы.

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

  • Серия200 означает «успех» - ваш запрос был действителен, и ответ логически следует из него.

  • Серия400 означает «неверный запрос» - что-то было не так с запросом, поэтому сервер не обработал его так, как вы хотели. Распространенными причинами ошибок уровня s HTTP400являются неверно отформатированные запросы и проблемы аутентификации.

  • Серия500 означает «ошибка сервера» - ваш запрос мог быть в порядке, но сервер не смог дать вам хороший ответ прямо сейчас по причинам, не зависящим от вас. Это должно быть редко, но вы должны знать о возможности, чтобы вы могли обрабатывать их в своем коде.

Ваш код должен всегда проверять код состояния HTTP для любого ответа, прежде чем пытаться что-либо с ним делать. Если вы этого не сделаете, вы потеряете время на устранение неполадок с неполной информацией.

Теперь, когда у вас есть общее представление о том, как отправить запрос и что искать в ответе, пришло время отправить этот первый запрос.

[[step-2 -—- Get-information-from-the-web-api]] == Шаг 2 - Получение информации из веб-API

Ваша учетная запись DigitalOcean содержит некоторую административную информацию, которую вы, возможно, не видели в веб-интерфейсе. API может дать вам другое представление о знакомой информации. Простое видение этого альтернативного представления иногда может зажечь идеи о том, что вы, возможно, захотите сделать с помощью API, или выявить службы и опции, о которых вы не знали.

Давайте начнем с создания проекта для наших скриптов. Создайте новый каталог для проекта с именемapis:

mkdir apis

Затем перейдите в этот новый каталог:

cd apis

Создайте новый virtualenv для этого проекта:

python3 -m venv apis

Активируйте virtualenv:

source apis/bin/activate

Затем установите библиотекуrequests, которую мы будем использовать в наших скриптах для выполнения HTTP-запросов в наших скриптах:

pip install requests

Настроив среду, создайте новый файл Python с именемdo_get_account.py и откройте его в текстовом редакторе. Запустите эту программу сimporting libraries для работы с JSON и HTTP запросами.

do_get_account.py

import json
import requests

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

Затем мы хотим настроить несколькоvariables для хранения информации, которая будет одинаковой в каждом запросе. Это избавляет нас от необходимости вводить его снова и снова и дает нам единственное место для обновления в случае каких-либо изменений. Добавьте эти строки в файл после операторовimport.

do_get_account.py

...
api_token = 'your_api_token'
api_url_base = 'https://api.digitalocean.com/v2/'

Переменнаяapi_token - это строка, содержащая ваш токен API DigitalOcean. Замените значение в примере своим собственным токеном. Переменнаяapi_url_base - это строка, с которой начинается каждый URL-адрес в DigitalOcean API. Мы добавим его по мере необходимости позже в коде.

Далее нам нужно настроить заголовки HTTP-запроса, как описано в документации API. Добавьте эти строки в файл, чтобы настроитьdictionary, содержащий заголовки ваших запросов:

do_get_account.py

...
headers = {'Content-Type': 'application/json',
           'Authorization': 'Bearer {0}'.format(api_token)}

Это устанавливает два заголовка одновременно. ЗаголовокContent-Type сообщает серверу, что данные в формате JSON должны находиться в теле запроса. ЗаголовокAuthorization должен включать наш токен, поэтому мы используем логику форматирования строки Python для вставки нашей переменнойapi_token в строку при ее создании. Мы могли бы поместить токен здесь как буквальную строку, но его разделение облегчает несколько вещей в будущем:

  • Если вам необходимо заменить токен, легче увидеть, где это сделать, если это отдельная переменная.

  • Если вы хотите поделиться своим кодом с кем-то, вам будет проще удалить ваш API-токен, и вашему другу будет проще увидеть, где их разместить.

  • Это самодокументирование. Если маркер API используется только в качестве строкового литерала, то кто-то, читающий ваш код, может не понять, на что он смотрит.

Теперь, когда у нас есть эти детали настройки, пришло время фактически отправить запрос. Возможно, вы захотите просто начать создавать и отправлять запросы, но есть и лучший способ. Если вы поместите эту логику в функцию, которая обрабатывает отправку запроса и чтение ответа, вам придется немного более четко подумать о том, что вы делаете. Вы также получите код, который сделает тестирование и повторное использование более простым. Это то, что мы собираемся сделать.

Эта функция будет использовать переменные, которые вы создали, чтобы отправить запрос и вернуть информацию об учетной записи в словаре Python.

Чтобы сохранить ясную логику на этом раннем этапе, мы пока не будем детально обрабатывать ошибки, но добавим это достаточно скоро.

Определите функцию, которая извлекает информацию об учетной записи. Всегда полезно называть функцию в честь того, что она делает: эта функция получает информацию об аккаунте, поэтому мы назовем ееget_account_info:

do_get_account.py

...
def get_account_info():

    api_url = '{0}account'.format(api_url_base)

    response = requests.get(api_url, headers=headers)

    if response.status_code == 200:
        return json.loads(response.content.decode('utf-8'))
    else:
        return None

Мы создаем значение дляapi_url, используя метод форматирования строк Python, аналогичный тому, как мы использовали его в заголовках; мы добавляем базовый URL API перед строкойaccount, чтобы получить URLhttps://api.digitalocean.com/v2/account, URL, который должен возвращать информацию об учетной записи.

Переменнаяresponse содержит объект, созданный Pythonrequestsmodule. Эта строка отправляет запрос на URL, который мы сделали с заголовками, которые мы определили в начале скрипта, и возвращает ответ от API.

Далее мы рассмотрим код состояния HTTP ответа.

Если это200, успешный ответ, то мы используем функциюloads модуляjson для загрузки строки как JSON. Загружаемая строка - это содержимое объектаresponse,response.content. Часть.decode('utf-8') сообщает Python, что этот контент кодируется с использованием набора символов UTF-8, как и все ответы от DigitalOcean API. Модульjson создает объект из этого, который мы используем в качестве возвращаемого значения для этой функции.

Если ответ былnot200, тогда мы возвращаемNone, которое является специальным значением в Python, которое мы можем проверить при вызове этой функции. Вы заметите, что на данный момент мы просто игнорируем любые ошибки. Это сделано для того, чтобы логика «успеха» была ясной. Мы добавим более полную проверку ошибок в ближайшее время.

Теперь вызовите эту функцию, убедитесь, что она получила хороший ответ, и распечатайте детали, которые возвратил API:

do_get_account.py

...
account_info = get_account_info()

if account_info is not None:
    print("Here's your info: ")
    for k, v in account_info['account'].items():
        print('{0}:{1}'.format(k, v))

else:
    print('[!] Request Failed')

account_info = get_account_info() устанавливает переменнуюaccount_info для того, что вернулось из вызова вget_account_info(), поэтому это будет либо специальное значениеNone, либо сбор информации о учетная запись.

Если это неNone, то мы распечатываем каждую часть информации в отдельной строке, используя методitems(), который есть во всех словарях Python.

В противном случае (то есть, еслиaccount_info равноNone), мы печатаем сообщение об ошибке.

Давайте на минуту остановимся здесь. Этот операторif с двойным отрицанием вначале может показаться неудобным, но это обычная идиома Python. Его достоинство заключается в том, что код, который выполняется успешно, очень близко кconditional, а не после обработки ошибок.

Вы можете сделать это по-другому, если хотите, и это может быть хорошим упражнением - написать этот код самостоятельно. Вместоif account_info is not None: вы можете начать сif account_info is None: и посмотреть, как все остальное встанет на свои места.

Сохраните скрипт и попробуйте:

python do_get_account.py

Вывод будет выглядеть примерно так:

OutputHere's your info:
droplet_limit:25
email:[email protected]
status:active
floating_ip_limit:3
email_verified:True
uuid:123e4567e89b12d3a456426655440000
status_message:

Теперь вы знаете, как получить данные из API. Далее мы перейдем к чему-то более интересному - с помощью API для изменения данных.

[[step-3 -—- modifying-information-on-the-server]] == Шаг 3 - изменение информации на сервере

После практики с запросом только для чтения, пришло время начать вносить изменения. Давайте рассмотрим это с помощью Python и API DigitalOcean, чтобы добавить SSH-ключ к вашей учетной записи DigitalOcean.

Во-первых, взгляните на документацию API для ключей SSH, доступную по адресуhttps://developers.digitalocean.com/documentation/v2/#ssh-keys.

API позволяет перечислять текущие ключи SSH в вашей учетной записи, а также позволяет добавлять новые. Запрос на получение списка ключей SSH очень похож на запрос на получение информации об учетной записи. Ответ отличается, однако: в отличие от учетной записи, вы можете иметь ноль, один или несколько ключей SSH.

Создайте новый файл для этого сценария с именемdo_ssh_keys.py и запустите его точно так же, как и предыдущий. Импортируйте модулиjson иrequests, чтобы не беспокоиться о деталях JSON или протокола HTTP. Затем добавьте свой токен DigitalOcean API в качестве переменной и настройте заголовки запросов в словаре.

do_ssh_keys.py

import json
import requests


api_token = 'your_api_token'
api_url_base = 'https://api.digitalocean.com/v2/'
headers = {'Content-Type': 'application/json',
           'Authorization': 'Bearer {0}'.format(api_token)}

Функция, которую мы создадим для получения ключей SSH, аналогична той, которую мы использовали для получения информации об учетной записи, но на этот раз мы собираемся обрабатывать ошибки более напрямую.

Сначала мы выполним вызов API и сохраним ответ в переменной ответаresponse. Однакоapi_url не будет таким же, как в предыдущем скрипте; на этот раз он должен указать наhttps://api.digitalocean.com/v2/account/keys.

Добавьте этот код в скрипт:

do_ssh_keys.py

...
def get_ssh_keys():

    api_url = '{0}account/keys'.format(api_url_base)

    response = requests.get(api_url, headers=headers)

Теперь давайте добавим некоторую обработку ошибок, посмотрев на код состояния HTTP в ответе. Если это200, мы вернем содержимое ответа в виде словаря, как и раньше. Если это что-то еще, мы напечатаем полезное сообщение об ошибке, связанное с типом кода состояния, а затем вернемNone.

Добавьте эти строки в функциюget_ssh_keys:

do_ssh_keys.py

...

    if response.status_code >= 500:
        print('[!] [{0}] Server Error'.format(response.status_code))
        return None
    elif response.status_code == 404:
        print('[!] [{0}] URL not found: [{1}]'.format(response.status_code,api_url))
        return None
    elif response.status_code == 401:
        print('[!] [{0}] Authentication Failed'.format(response.status_code))
        return None
    elif response.status_code == 400:
        print('[!] [{0}] Bad Request'.format(response.status_code))
        return None
    elif response.status_code >= 300:
        print('[!] [{0}] Unexpected Redirect'.format(response.status_code))
        return None
    elif response.status_code == 200:
        ssh_keys = json.loads(response.content.decode('utf-8'))
        return ssh_keys
    else:
        print('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content))
    return None

Этот код обрабатывает шесть различных состояний ошибки, просматривая код состояния HTTP в ответе.

  • Код500 или больше указывает на проблему на сервере. Это должно быть редко, и они не вызваны проблемами с запросом, поэтому мы печатаем только код состояния.

  • Код404 означает «не найден», что, вероятно, связано с опечаткой в ​​URL-адресе. Для этой ошибки мы печатаем код состояния и URL, который привел к нему, чтобы вы могли понять, почему это не удалось.

  • Код401 означает, что аутентификация не удалась. Наиболее вероятная причина - неверный или отсутствующийapi_key.

  • Код в диапазоне300 указывает на перенаправление. API-интерфейс DigitalOcean не использует перенаправления, поэтому этого никогда не должно происходить, но пока мы обрабатываем ошибки, проверка не повредит. Многие ошибки вызваны вещами, которые, по мнению программиста, никогда не должны происходить.

  • Код200 означает, что запрос был успешно обработан. Для этого мы ничего не печатаем. Мы просто возвращаем ключи ssh как объект JSON, используя тот же синтаксис, который мы использовали в предыдущем скрипте.

  • Если код ответа был чем-то другим, мы печатаем код состояния как «неожиданную ошибку».

Это должно обрабатывать любые ошибки, которые мы, вероятно, получим при вызове API. На данный момент у нас есть либо сообщение об ошибке и объектNone, либо у нас есть успех и объект JSON, содержащий ноль или более ключей SSH. Наш следующий шаг - распечатать их:

do_ssh_keys.py

...

ssh_keys = get_ssh_keys()

if ssh_keys is not None:
    print('Here are your keys: ')
    for key, details in enumerate(ssh_keys['ssh_keys']):
        print('Key {}:'.format(key))
        for k, v in details.items():
            print('  {0}:{1}'.format(k, v))
else:
    print('[!] Request Failed')

Поскольку ответ содержитlist (или массив) ключей SSH, мы хотим перебрать весь список, чтобы увидеть все ключи. Для этого мы используем метод Pythonenumerate. Это похоже на методitems, доступный для словарей, но вместо этого он работает со списками.

Мы используемenumerate, а не толькоfor loop, потому что мы хотим иметь возможность сказать, насколько далеко мы находимся в списке для любого заданного ключа.

Информация о каждом ключе возвращается в виде словаря, поэтому мы используем тот же кодfor k,v in details.items():, который мы использовали в словаре информации учетной записи в предыдущем скрипте.

Запустите этот скрипт, и вы получите список ключей SSH, уже имеющихся в вашей учетной записи.

python get_ssh_keys.py

Вывод будет выглядеть примерно так, в зависимости от того, сколько ключей SSH у вас уже есть в вашей учетной записи.

OutputHere are your keys:
Kcy 0:
  id:280518
  name:work
  fingerprint:96:f7:fb:9f:60:9c:9b:f9:a9:95:01:5c:5c:2c:d5:a0
  public_key:ssh-rsa AAAAB5NzaC1yc2cAAAADAQABAAABAQCwgr9Fzc/YTD/V2Ka5I52Rx4I+V2Ka5I52Rx4Ir5LKSCqkQ1Cub+... sammy@work
Kcy 1:
  id:290536
  name:home
  fingerprint:90:1c:0b:ac:fa:b0:25:7c:af:ab:c5:94:a5:91:72:54
  public_key:ssh-rsa AAAAB5NzaC1yc2cAAAABJQAAAQcAtTZPZmV96P9ziwyr5LKSCqkQ1CubarKfK5r7iNx0RNnlJcqRUqWqSt... sammy@home

Теперь, когда вы можете перечислить ключи SSH в своей учетной записи, ваш последний скрипт будет тем, который добавляет новый ключ в список.

Прежде чем мы сможем добавить новый ключ SSH, нам нужно сгенерировать его. Для более полного рассмотрения этого шага, взгляните на руководствоHow to Set Up SSH Keys.

Однако для наших целей нам просто нужен простой ключ. Выполните эту команду, чтобы сгенерировать новую в Linux, BSD или MacOS. Вы можете сделать это на существующей капле, если хотите.

ssh-keygen -t rsa

При появлении запроса введите файл для сохранения ключа и не вводите ключевую фразу.

OutputGenerating public/private rsa key pair.
Enter file in which to save the key (/home/sammy/.ssh/id_rsa): /home/sammy/.ssh/sammy
Created directory '/home/sammy/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/sammy/.ssh/sammy.
Your public key has been saved in /home/sammy/.ssh/sammy.pub.
...

Запомните, где был сохранен файл открытого ключа, потому что он понадобится вам для сценария.

Запустите новый скрипт Python, назовите егоadd_ssh_key.py и запустите его, как и другие:

add_ssh_key.py

import json
import requests


api_token = 'your_api_token'
api_url_base = 'https://api.digitalocean.com/v2/'
headers = {'Content-Type': 'application/json',
           'Authorization': 'Bearer {0}'.format(api_token)}

Мы будем использовать функцию, чтобы сделать наш запрос, но этот будет немного другим.

Создайте функцию с именемadd_ssh_key, которая будет принимать два аргумента: имя, используемое для нового ключа SSH, и имя файла самого ключа в вашей локальной системе. Функция будетread the file и сделает запрос HTTPPOST вместоGET:

add_ssh_key.py

...

def add_ssh_key(name, filename):

    api_url = '{0}account/keys'.format(api_url_base)

    with open(filename, 'r') as f:
        ssh_key = f.readline()

    ssh_key = {'name': name, 'public_key': ssh_key}

    response = requests.post(api_url, headers=headers, json=ssh_key)

Строкаwith open(filename, 'r') as f: открывает файл в режиме только для чтения, а следующая за ней строка считывает первую (и единственную) строку из файла, сохраняя ее в переменнойssh_key.

Затем мы создаем словарь Python с именемssh_key с именами и значениями, которые ожидает API.

Однако, когда мы отправляем запрос, появляется немного больше нового. ЭтоPOST, а неGET, и нам нужно отправитьssh_key в теле запросаPOST в кодировке JSON. Модульrequests обработает детали за нас; requests.post указывает ему использовать методPOST, а включениеjson=ssh_key указывает ему отправить переменнуюssh_key в теле запроса в кодировке JSON.

Согласно API, в случае успеха ответом будет HTTP201 вместо200, а тело ответа будет содержать детали только что добавленного ключа.

Добавьте следующий код обработки ошибок в функциюadd_ssh_key. Он похож на предыдущий сценарий, за исключением того, что на этот раз для успеха нам нужно искать код201 вместо200:

add_ssh_key.py

...
    if response.status_code >= 500:
        print('[!] [{0}] Server Error'.format(response.status_code))
        return None
    elif response.status_code == 404:
        print('[!] [{0}] URL not found: [{1}]'.format(response.status_code,api_url))
        return None
    elif response.status_code == 401:
        print('[!] [{0}] Authentication Failed'.format(response.status_code))
        return None
    elif response.status_code >= 400:
        print('[!] [{0}] Bad Request'.format(response.status_code))
        print(ssh_key )
        print(response.content )
        return None
    elif response.status_code >= 300:
        print('[!] [{0}] Unexpected redirect.'.format(response.status_code))
        return None
    elif response.status_code == 201:
        added_key = json.loads(response.content)
        return added_key
    else:
        print('[?] Unexpected Error: [HTTP {0}]: Content: {1}'.format(response.status_code, response.content))
        return None

Эта функция, как и предыдущие, возвращает либоNone, либо содержимое ответа, поэтому мы используем тот же подход, что и раньше, для проверки результата.

Затем вызовите функцию и обработайте результат. Передайте путь к вновь созданному ключу SSH в качестве второго аргумента:

add_ssh_key.py

...
add_response = add_ssh_key('tutorial_key', '/home/sammy/.ssh/sammy.pub')

if add_response is not None:
    print('Your key was added: ' )
    for k, v in add_response.items():
        print('  {0}:{1}'.format(k, v))
else:
    print('[!] Request Failed')

Запустите этот сценарий, и вы получите ответ о том, что ваш новый ключ добавлен.

python add_ssh_key.py

Вывод будет выглядеть примерно так:

OutputYour key was added:
  ssh_key:{'id': 9458326, 'name': 'tutorial_key', 'fingerprint': '64:76:37:77:c8:c7:26:05:f5:7b:6b:e1:bb:d6:80:da', 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCUtY9aizEcVJ65/O5CE6tY8Xodrkkdh9BB0GwEUE7eDKtTh4NAxVjXc8XdzCLKtdMwfSg9xwxSi3axsVWYWBUhiws0YRxxMNTHCBDsLFTJgCFC0JCmSLB5ZEnKl+Wijbqnu2r8k2NoXW5GUxNVwhYztXZkkzEMNT78TgWBjPu2Tp1qKREqLuwOsMIKt4bqozL/1tu6oociNMdLOGUqXNrXCsOIvTylt6ROF3a5UnVPXhgz0qGbQrSHvCEfuKGZ1kw8PtWgeIe7VIHbS2zTuSDCmyj1Nw1yOTHSAqZLpm6gnDo0Lo9OEA7BSFr9W/VURmTVsfE1CNGSb6c6SPx0NpoN sammy@tutorial-test'}

Если вы забыли изменить условие «успеха» на поиск HTTP201 вместо200, вы увидите сообщение об ошибке, но ключ все равно будет добавлен. Ваша обработка ошибок сообщила бы вам, что код состояния был201. Вы должны признать это как член серии200, что указывает на успех. Это пример того, как базовая обработка ошибок может упростить устранение неполадок.

После того, как вы успешно добавили ключ с помощью этого сценария, запустите его еще раз, чтобы посмотреть, что произойдет, когда вы попытаетесь добавить уже существующий ключ.

API отправит ответ HTTP422, который ваш скрипт переведет в сообщение о том, что «SSH-ключ уже используется в вашей учетной записи»:

Output[!] [422] Bad Request
{'name': 'tutorial_key', 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCUtY9aizEcVJ65/O5CE6tY8Xodrkkdh9BB0GwEUE7eDKtTh4NAxVjXc8XdzCLKtdMwfSg9xwxSi3axsVWYWBUhiws0YRxxMNTHCBDsLFTJgCFC0JCmSLB5ZEnKl+Wijbqnu2r8k2NoXW5GUxNVwhYztXZkkzEMNT78TgWBjPu2Tp1qKREqLuwOsMIKt4bqozL/1tu6oociNMdLOGUqXNrXCsOIvTylt6ROF3a5UnVPXhgz0qGbQrSHvCEfuKGZ1kw8PtWgeIe7VIHbS2zTuSDCmyj1Nw1yOTHSAqZLpm6gnDo0Lo9OEA7BSFr9W/VURmTVsfE1CNGSb6c6SPx0NpoN sammy@tutorial-test'}
b'{"id":"unprocessable_entity","message":"SSH Key is already in use on your account"}'
[!] Request Failed

Теперь запустите ваш скриптget_ssh_keys.py еще раз, и вы увидите новый ключ в списке.

С небольшими изменениями эти два скрипта могут стать быстрым способом добавления новых ключей SSH в вашу учетную запись DigitalOcean, когда вам это необходимо. Связанные функции этого API позволяют переименовывать или удалять определенный ключ, используя его уникальный идентификатор ключа или отпечаток пальца.

Давайте посмотрим на другой API и посмотрим, как переводятся приобретенные вами навыки.

[[step-4 -—- working-with-a-different-api]] == Шаг 4. Работа с другим API

У GitHub тоже есть API. Все, что вы узнали об использовании API DigitalOcean, непосредственно применимо к использованию GitHub API.

Познакомьтесь с GitHub API так же, как и с DigitalOcean. НайдитеAPI documentation и просмотрите разделOverview. Вы сразу увидите, что API GitHub и API DigitalOcean имеют некоторые общие черты.

Во-первых, вы заметите, что у всех URL-адресов API есть общий корень:https://api.github.com/. Вы знаете, как использовать это как переменную в своем коде, чтобы упростить и уменьшить вероятность ошибок.

API GitHub использует JSON в качестве формата запросов и ответов, как и DigitalOcean, чтобы вы знали, как выполнять эти запросы и обрабатывать ответы.

Ответы включают информацию об ограничениях скорости в заголовках ответа HTTP, используя почти те же имена и точно такие же значения, что и DigitalOcean.

GitHub использует OAuth для аутентификации, и вы можете отправить свой токен в заголовке запроса. Детали этого токена немного отличаются, но способ его использования идентичен тому, как вы это сделали с API DigitalOcean.

Есть и некоторые отличия. GitHub рекомендует использовать заголовок запроса, чтобы указать версию API, которую вы хотите использовать. Вы знаете, как добавить заголовки к запросам в Python.

GitHub также хочет, чтобы вы использовали уникальную строкуUser-Agent в запросах, чтобы они могли легче найти вас, если ваш код вызывает проблемы. Вы бы справились и с заголовком.

API GitHub использует те же методы HTTP-запроса, но также использует новый метод под названиемPATCH для определенных операций. API GitHub используетGET для чтения информации,POST для добавления нового элемента иPATCH для изменения существующего элемента. Этот запросPATCH - это то, что вам нужно искать в документации по API.

Не все вызовы GitHub API требуют аутентификации. Например, вы можете получить список репозиториев пользователя, не нуждаясь в токене доступа. Давайте создадим скрипт для выполнения этого запроса и отображения результатов.

Мы упростим обработку ошибок в этом скрипте и используем только один оператор для обработки всех возможных ошибок. Вам не всегда нужен код для обработки каждого типа ошибок в отдельности, но это хорошая привычка делать что-то с условиями ошибки, хотя бы для того, чтобы напомнить себе, что все не всегда происходит так, как вы ожидаете.

Создайте в редакторе новый файл с именемgithub_list_repos.py и добавьте следующий контент, который должен выглядеть довольно знакомо:

github_list_repos.py

import json
import requests


api_url_base = 'https://api.github.com/'
headers = {'Content-Type': 'application/json',
           'User-Agent': 'Python Student',
           'Accept': 'application/vnd.github.v3+json'}

Импорт тот же, который мы использовали. api_url_base - это то место, где начинаются все API GitHub.

Заголовки включают в себя два дополнительных, которые GitHub упоминает в своем обзоре, плюс тот, который говорит, что мы отправляем данные в формате JSON в нашем запросе.

Несмотря на то, что это небольшой скрипт, мы все равно определим функцию, чтобы сохранить нашу логику модульной и инкапсулировать логику для выполнения запроса. Часто ваши маленькие сценарии превращаются в более крупные, поэтому полезно быть усердным в этом. Добавьте функцию с именемget_repos, которая принимает имя пользователя в качестве аргумента:

github_list_repos.py

...
def get_repos(username):

    api_url = '{}orgs/{}/repos'.format(api_url_base, username)

    response = requests.get(api_url, headers=headers)

    if response.status_code == 200:
        return (response.content)
    else:
        print('[!] HTTP {0} calling [{1}]'.format(response.status_code, api_url))
        return None

Внутри функции мы создаем URL-адрес изapi_url_base, имени пользователя, который нас интересует, и статических частей URL-адреса, которые сообщают GitHub, что нам нужен список репозиториев. Затем мы проверяем код состояния HTTP ответа, чтобы убедиться, что это200 (успех). Если это было успешно, мы возвращаем содержание ответа. Если это не так, то мы распечатываем фактический код статуса и созданный нами URL-адрес, чтобы у нас было представление о том, где мы могли ошибиться.

Теперь вызовите функцию и передайте имя пользователя GitHub, которое вы хотите использовать. В этом примере мы будем использоватьoctokit. Затем распечатайте результаты на экране:

github_list_repos.py

...
repo_list = get_repos('octokit')

if repo_list is not None:
    print(repo_list)
else:
    print('No Repo List Found')

Сохраните файл и запустите скрипт, чтобы увидеть репозитории для указанного вами пользователя.

python github_list_repos.py

Вы увидите много данных в выходных данных, потому что мы не проанализировали ответ как JSON в этом примере и не отфильтровали результаты по конкретным ключам. Используйте то, что вы узнали в других скриптах, чтобы сделать это. Посмотрите на результаты, которые вы получаете, и посмотрите, сможете ли вы распечатать имя хранилища.

Одна из приятных особенностей этих GitHub API заключается в том, что вы можете получать доступ к запросам, для которых не требуется аутентификация, непосредственно в веб-браузере. Это позволяет сравнивать ответы с тем, что вы видите в своих скриптах. Попробуйте посетитьhttps://api.github.com/orgs/octokit/repos в своем браузере, чтобы увидеть там ответ.

К настоящему времени вы знаете, как читать документацию и писать код, необходимый для отправки более конкретных запросов для поддержки ваших собственных целей с помощью API GitHub.

Вы можете найти законченный код для всех примеров в этом руководстве вthis repository on GitHub.

Заключение

В этом руководстве вы узнали, как использовать веб-API для двух разных сервисов с немного разными стилями. Вы увидели важность включения кода обработки ошибок, чтобы упростить отладку и повысить надежность сценариев. Вы использовали модули Pythonrequests иjson, чтобы изолировать вас от деталей этих технологий и просто выполнить свою работу, и вы инкапсулировали обработку запросов и ответов в функции, чтобы сделать ваши скрипты более модульными.

И более того, теперь у вас есть повторяемый процесс, которому нужно следовать при изучении любого нового веб-API:

  1. Найдите документацию и прочитайте введение, чтобы понять основы взаимодействия с API.

  2. Получите токен аутентификации, если он вам нужен, и напишите модульный скрипт с базовой обработкой ошибок, чтобы отправить простой запрос, ответить на ошибки и обработать ответ.

  3. Создайте запросы, которые будут получать вам информацию, которую вы хотите от службы.

Теперь закрепите полученные знания и найдите другой API для использования или даже другую функцию одного из API, которые вы использовали здесь. Ваш собственный проект поможет закрепить то, что вы узнали здесь.

Related