Наша команда занимается созданием информационного сервиса по отображению глобальных данных для многих стран, городов и территорий — Routitude. К концу февраля этого года стремительное распространение коронавируса по всему миру побудило нас внедрить дополнительный функционал для мониторинга ситуации в наше приложение. Помимо визуализации данных в веб-интерфейсе, основным компонентом реализации этой задачи стал микросервис, написанный на Python с использованием популярного веб-фреймворка Flask. Сервис регулярно обновляет данные из различных источников и по запросу отдает необходимую информацию для визуализации в веб-интерфейсе. Основным источником данных являются страницы Википедии, посвященные распространению вируса в странах и территориях. Таблицы с показателями на этих страницах оперативно обновляются и отлично подходят в качестве источника данных для сервиса по мониторингу распространения инфекции. В статье я расскажу про основные компоненты сервиса, от получения и обновления данных до создания API для клиентских запросов. Код проекта доступен в github репозитории. Проект может быть полезным тем, кто интересуется созданием микросервисов на Python с помощью Flask фреймворка и хочет опробовать навыки на базе реальной задачи. Также, в статье описаны простые способы получения данных из Википедии, которые могут быть применены для обогащения информацией того или иного приложения. В целом, проект готов к использованию для создания собственного сервиса по мониторингу распространения инфекции COVID-19. Для реализации проекта нам понадобится Python и любая база данных, которая поддерживается популярной библиотекой для описания ORM моделей в Python SQLAlchemy. В Routitude мы используем PostgreSQL. Первым делом необходимо клонировать репозиторий с проектом и установить необходимые зависимости: Для успешного взаимодействия с базой данных нужно установить переменную окружения с URI до нее: На этом вся необходимая конфигурация заканчивается. Теперь давайте познакомимся со структурой проекта: Итого, наш сервис состоит из четырех основных компонент: Рассмотрим их подробнее в следующих разделах. Перед каждым блоком кода будет приведена ссылка на соответствующий файл в github репозитории, в котором можно глубже погрузится в проект и изучить все вспомогательные методы, неупомянутые в статье. Данные по распространению коронавируса регулярно обновляются на соответствующих страницах в Википедии и хорошо структурированы в виде таблиц. Ознакомиться с содержимым интересующих нас страниц можно по ссылкам: Нас интересуют данные из таблиц со следующими показателями: Одним из самых простых способов получения таблиц с html страницы является метод read_html из популярного пакета для анализа данных Pandas. Данный метод возвращает список всех найденных таблиц на странице. Обычно нас интересует конкретная таблица. Мы можем понять ее порядок, однако, он может легко измениться при редактировании страницы. При этом, часть содержимого текста в таблице будет меняться значительно реже, чем ее порядок. Таким образом, мы можем идентифицировать нужную таблицу по вхождению в нее определенного текста. Ниже приведен метод для получения Pandas DataFrame, содержащего данные из таблицы, найденной на заданной html странице по вхождению указанной строки. datasources/utils.py Будем использовать описанный метод для получения необходимых данных с каждой из интересующих нас страниц. Следующий блок кода содержит метод возвращающий таблицу со статистикой распространения вируса по странам и территориям. По аналогии, модуль содержит методы для получения подобных данных по распространению инфекции COVID-19 в отдельных административных субъектах России и США. datasources/covid_wiki.py Для того чтобы оперативно реагировать на изменения в структуре html страниц и, в целом, для более комфортной дальнейшей разработки, покроем тестами методы получения данных. datasources/test/test_covid.py Для запуска тестов можно воспользоваться следующей командой: После того как мы научились собирать данные из источника, самое время подумать о том, как их хранить и обновлять. Популярным решением этой проблемы является создание связи между Python объектами, описывающими данные, и таблицами в базе данных, которые их хранят. Формально это называется объектно-реляционное отображение (Object-Relational Mapping, ORM). Классическим инструментом в Python для создания ORM модели является библиотека SQLAlchemy. Помимо нее, нам понадобится инструмент для создания миграций базы данных, который берет на себя всю работу по созданию, редактированию и версионированию таблиц в базе данных на основе SQLAchemy объектов. С такими задачами отлично справляется пакет Alembic. Но мы пойдем выше и будем использовать обертку над ним, специально созданную для Flask приложений, Flask-Migrate. Конфигурация данного инструмента сводится к инициализации нескольких объектов в appvars.py и manage.py. Для того чтобы создать ORM модель необходимо унаследовать класс от объекта Model из пакета SQLAlchemy. Далее требуется описать каждое поле в таблице в виде атрибутов класса с помощью объектов SQLAchemy для разных типов полей. Помимо этого, можно задать индексы на указанные поля как показано в примере ниже. В этом примере создается ORM модель для таблицы с данными о распространение коронавируса в различных странах и территориях. models.py Если вы не работаете с готовым проектом из репозитория, а начали проект с нуля, прежде всего необходимо инициализировать хранилище миграций на базе Alembic: Эта команда создаст папку migrations со всем необходимым для дальнейшей работы содержимым. Для того чтобы создать миграции для базы данных на основе описанной ORM модели нужно выполнить следующую команду: При использовании готового проекта из репозитория, исполнять приведенные выше команды не нужно, так как они уже были выполнены и, следовательно, код для создания в базе данных объектов, соответствующих описанной модели, был сгенерирован. Этот код находится в папке migrations/versions. Она, в свою очередь, хранит набор скриптов python, соответствующих версиям состояний базы данных. Каждый файл состоит из 2 методов — upgrate и downgrade. Первый метод трансформирует содержимое базы данных к текущей версии ORM модели, а второй реализует набор действий с базой данных для того, чтобы привести ее к состоянию предыдущей версии. Предыдущая команда генерирует код для создания миграции для текущего состояния, но не приводит его в действие. Для того чтобы применить изменения и исполнить код текущей миграции необходимо выполнить команду: Теперь все необходимые таблицы и индексы в базе данных созданы и мы готовы пополнять ее данными по распространению коронавируса. Для обновления данных в БД создадим метод в классе ORM модели, который будет принимать pandas DataFrame с данными по коронавирусу, сравнивать записи в нем с записями в базе данных и, если они изменились или не нашлись, записывать показатели в таблицу. Помимо основной статистики, будем фиксировать время последнего обновления данных по каждой записи в таблице. Для всех операций, связанных с получением данных из БД и записи в нее, используются вспомогательные методы SQLAlchemy.  models.py Далее объединим получение данных по странам и административным субъектам России и США в один метод для обновления показателей из всех отчетов. Для этого будем последовательно вызывать вышеописанный метод и передавать в него результат получения данных из Википедии для каждого из отчетов. models.py Расширение для Flask приложений Flask-Script позволяет нам быстро и удобно создать простой консольный интерфейс для запуска различных скриптов из командной строки. Используя это пакет для того, чтобы превратить метод в исполняемый скрипт, достаточно добавить декоратор manager.command перед его объявлением. Воспользуемся этим инструментом для создания скрипта обновления данных в БД. manage.py Теперь, для получения и обновления данных в ДБ, достаточно выполнить следующую команду: Выполнение этой команды имеет смысл запускать с использованием какого-либо инструмента для автоматического исполнения заданий по расписанию, например cron. Основной целью сервиса является обработка клиентских запросов и формирование ответов с требуемой информацией по распространению коронавируса. Пришло время реализовать этот функционал в нашем Flask приложении. Как мы уже выяснили, SQLAlchemy предоставляет набор удобных инструментов для получения записей из базы данных. Воспользуемся ими и создадим набор методов, возвращающих следующие статистические показатели распространения инфекции COVID-19: api/covid.py Завершим разработку приложения описанием конкретных ресурсов сервиса для каждого из методов API. Сделать это можно с помощью специальных декораторов, задающих адрес метода, по которому будет доступен соответсвующий ресурс. app.py Остается запустить приложение и протестировать его работоспособность на базе сервера Flask для разработки. Теперь сервис доступен на указанном в выводе порту, и вы можете проверить корректность его работы, запрашивая ресурсы по заданным адресам в браузере или другими инструментами, например curl. В проекте предусмотрено тестирование приложения. Тесты находятся в директории test, а запустить их можно следующей командой из корня проекта: Для промышленной эксплуатации запускайте сервис на базе промышленного веб-сервера. Популярными вариантами таких серверов для Python приложений являются gunicorn и uWSGI. Описанный в статье сервис решает задачу сбора и обновления данных по распространению коронавируса в различных странах и территориях, а также административных субъектах России и США. Он позволяет обрабатывать клиентские запросы посредством API для предоставления информации, содержащей базовые показатели по распространению вируса. Данные из Википедии отлично подходят для отражения текущей ситуации, так как они оперативно обновляются и соответствуют официальным источникам по всем формальным признакам. В Routitude мы используем этот сервис для отображения текущих показателей по распространению инфекции COVID-19 на карте и дашбордах. Все исходные материалы доступны в github репозитории. Приветствуются любые доработки, багфиксы, новые фичи и данные. Я буду рад любым замечаниям по статье и предложениям по улучшению проекта. Разработчик