Лучшие практики работы с API в реальном мире

Делать запросы к API в Python очень просто. Сделать это надежным, чистым и масштабируемым способом — совсем другая история.

вступление

Python прославился за последние 5–10 лет и в настоящее время является одним из самых популярных языков программирования в мире. Его легко освоить из-за его высокоуровневого (в стиле псевдокода) синтаксиса, и это позволяет легко начать писать действительно полезные скрипты. Для новичка это действительно мощно и заставляет вас чувствовать, что вы создаете ценность на раннем этапе своего учебного процесса.

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

Конечно, вы можете запустить свой скрипт в изолированной среде (на вашем ноутбуке), но что произойдет, когда он разрастется, и вы захотите начать его развертывание в облачной функции или в виде контейнера Docker?

Если мы не думаем о том, как создавать «хороший» (субъективный) код с самого начала, мы создаем головную боль для себя в будущем или других, которые должны взять на себя ответственность.

Одна вещь, которую я заметил во время своего опыта изучения Python, заключается в том, что очень немногие ресурсы проходят через «реальные» приложения использования языка в Data Engineering. Существует множество ресурсов для изучения основ (условия, классы, функции, циклы и т. д.). Есть и другие, которые показывают, как вы применяете их в действии, на базовых примерах (создание класса Animal или Vehicle и создание некоторых экземпляров). Некоторые более продвинутые показывают, как использовать Python для создания веб-приложений или игр или множества других забавных вещей. Но ни один из них не является особенно полезным с точки зрения Data Engineering.

Это первая статья из довольно забавной серии реальных примеров приложений Python, вы можете увидеть их все здесь:



Фон

Хорошее место для начала — работа с API. Это основа большей части того, что мы делаем в Data Engineering. Есть некоторые данные, которые существуют для части программного обеспечения или третьей стороны, которые нам нужно извлечь, и нам предоставляется документация о том, как их использовать.

В этом примере мы будем использовать API Рика и Морти…

Большинство «хороших» API для извлечения данных имеют общий набор функций:

  • Документация, чтобы рассказать вам, как с ним взаимодействовать — Документация Rick and Morty API
  • Определенная схема для результатов
  • Общее количество записей для объекта или конечной точки
  • Способ постраничного просмотра результатов
  • Метод фильтрации результатов
  • Аутентификация — мы не будем рассматривать это в этом руководстве, потому что API Рика и Морти открыто, я оставлю это для другого поста.

Методология

Давайте разберем процесс, который мы будем использовать для успешного завершения этого проекта:

  1. Используйте классы данных для обработки схемы возвращаемых значений.
  2. Используйте классы данных для обработки схемы ввода параметров для API.
  3. Создайте функцию для взаимодействия с API
  4. Создайте функцию для разбиения на страницы всех результатов API.

Перед тем, как мы начнем, немного подготовим настройки среды:

  • Я использую WSL2 в Windows 22H2.
  • Я буду реализовывать это в Python 3.8.
  • Управление зависимостями будет осуществляться через pipenv, единственное требование для этого — requests и black как зависимость разработчика для автоформатирования (еще одна тема для обсуждения).

Вы можете прочитать больше о Pipenv и его настройке здесь:



Теперь давайте начнем! 🙌

I. Классы данных для результатов API

Я буду использовать конечную точку «Персонаж», чтобы продемонстрировать все в этом руководстве, которое содержит 826 записей на 42 страницах.

Первое, что мы сделаем, это обработаем структуру API.

Нам говорят, что общая структура ответа (ApiResponse) имеет два поля: info (ApiInfo) и results. Это список символов, и эта схема нам дана в документации (CharacterSchema).

Давайте продолжим и превратим всю эту информацию в классы данных в Python.

Мы создадим файл models.py, чтобы отделить определения нашей модели от остальной части проекта.

Этот сегмент кода показывает импорт, который мы будем использовать для всего файла (Optional не используется в CharacterSchema, но нам понадобится позже).

Мы преобразовали схему документации в класс данных Python.

Теперь давайте обработаем сам ответ API (все еще в пределах models.py ).

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

Метод __post_init__ — это удобный способ применить постобработку к объекту при его первом создании.

В этом случае мы делаем две вещи. После создания объекта ApiResponse:

  1. Распакуйте значения, предоставленные для info (которые, как мы знаем, будут словарем), чтобы создать объект ApiInfo, и перезапишите значение info внутри ApiResponse.
  2. Используйте генератор списков, чтобы создать список объектов CharacterSchema из исходного списка results и перезаписать значение results в пределах ApiResponse.

II. Классы данных для ввода API

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

В дополнение к фильтруемым полям есть дополнительный параметр page, который используется для нумерации страниц.

Это последний раздел models.py .

Все эти параметры установлены как Optional (это означает, что ни один из них или все они могут использоваться в одном запросе) со значением по умолчанию None.

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

III. Разбивка на страницы через данные API

Первое, что нам нужно настроить, — это способ отправки запроса данных API.

Давайте создадим файл с именем ramapi.py, который выглядит так:

Это простая, но изящная функция, которую мы можем использовать, чтобы делать все, что нам нужно, с Rick and Morty API. Он принимает значение endpoint (мы используем «Character» для этого урока, но мы можем расширить его на любой другой, который работает таким же образом) и значение params, которое является экземпляром нашего объекта ApiParameters.

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

Вызов функции raise_for_status() означает, что если произойдет что-либо, кроме успешного запроса, у нас будет возбуждено исключение.

Это заключительная часть проекта, которая объединяет все вместе, main.py.

Здесь у нас есть одна функция с именем get_all_paginated_results() и блок выполнения внизу, который запускается, когда мы вызываем файл. Вы можете видеть, что эта функция создает пустой список, перебирает все страницы, устанавливает значение params.page в номер страницы, а затем делает запрос и сохраняет результаты в списке. Наконец, он возвращает все результаты.

Порядок обработки такой:

  1. Создайте объект параметров — это может показаться излишним в этом примере, потому что он пустой, но, указав его здесь, он распространится на случай, когда требуется ввод параметра без какого-либо изменения порядка обработки. Кроме того, нам нужны параметры для функции разбивки на страницы, поэтому имеет смысл иметь ее здесь.
  2. Отправьте первоначальный запрос и верните ответ — это используется, чтобы мы могли получить общее количество страниц перед разбиением на страницы.
  3. Запустите функцию пагинации — используя количество страниц из предыдущего вызова и передав параметры.

Когда мы запускаем файл main.py, вывод показан ниже, поэтому мы можем видеть, что мы правильно вернули точное количество страниц и общее количество записей, которые мы видели ранее.

Заключительные мысли 💭

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

Я ясно дал понять, что мы обрабатываем только первую часть процесса — получение данных. В реальном мире вы хотите что-то с этим сделать. Это может быть сохранение в файл или, в более реалистичном сценарии, сохранение в базе данных (или в Google Cloud Storage в виде BLOB).

В следующей части этой серии будет рассмотрен практический пример того, как именно вы могли бы это сделать! Прочтите это здесь:



Не стесняйтесь подписаться на Medium и LinkedIn, чтобы получить больше похожего контента, и обращайтесь с любыми вопросами, я люблю общаться с новыми людьми :)

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

Ссылка на репозиторий GitHub