Как писать код, если ты аналитик, или ETL своими руками

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Всем привет! Меня зовут Макс, и я продуктовый аналитик в Genesis. Последние пару лет работаю как аналитик в отечественном IT. До этого несколько лет учился и работал в Европе.

В статье расскажу о том, как собрать на коленке решение, которое позволит достать данные, изменить их и перенести с помощью кода (ETL), и о том, как на похожие простые решения опираются большие продукты. Может быть полезно начинающим не-разработчикам: аналитикам, маркетологам, продуктовым менеджерам и, отдельно, поклонникам Excel.

Процессы по переносу и изменению данных называют ETL-процессами (Extract Transform & Load), а решения для них — ETL-пайплайнами (pipelines). Такие решения включают в себя извлечение данных из источников, где на них неудобно смотреть, их агрегацию и обработку, а также загрузку в хранилища. Неплохая статья об ETL есть на «Хабре».

Какие решения требуют щепотки кода или полной автоматизации

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

  1. Автоматизировать доставку отчетов с высокоуровневыми метриками нескольким отделам по утрам.
  2. Распарсить гугл-таблички с ответами участников конференции. Сделать решение масштабируемым (у компании за год — 20 конференций). Связать ответы между собой и с данными от клиентов. Сохранить результат в удобном месте и визуализировать.
  3. Представить несколько ключевых метрик и инсайтов, предварительно вытащив данные из разных мест (по API, из облачного хранилища, добыв и расшифровав файл, хранившийся у коллеги на компе, обратившись в БД и так далее).
  4. Настроить систему уведомлений о состоянии баланса на внешнем сервисе отправки сообщений в вайбере. После реализации первой части создать визуализацию с динамикой затрат по часам.
  5. Автоматизировать сбор агрегированных таблиц в базе данных. Добавить логирование и настроить набор действий в случае, если часть агрегаций не собралась.

Большинство задач такого типа можно выполнить не выходя из дома, имея под рукой маленький удаленный сервер и будучи знакомым со Stack Overflow.

Инструменты для автоматизации задач

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

  • Наиболее популярное решение — Python. Хорошо применять для работы с данными, автоматизации задач на сервере и на личном компьютере. Плохо подходит для запуска на чужих машинах (попытки создать домашнее решение для импорта/превращения данных на чужих компьютерах на каком-либо языке программирования часто заканчиваются провалом).
  • Хороший и менее популярный инструмент аналитиков — R. Используют для тех же целей, что и Python, но субъективно R требует больше усилий для прототипирования и отладки простых программ (для джуна, если он, например, не имел раньше опыта ни с Python, ни с R).
  • Иногда можно встретить на JavaScript (или Google Script), которые позволяют вытаскивать данные в удобном виде в Google Sheets. Можно автоматизировать бесплатно на ресурсах Google и доставать информацию из баз данных/по API/из самих Google таблиц, попутно отправляя в чаты/на электронные почты оповещения об успехах (отправлять можно даже отдельные простые метрики). Такие решения не сильно популярны и имеют ряд ограничений.
  • Макросы в Excel. Отдельный способ достать данные из хранилища и визуализировать прямо в Excel-файле. Хороший способ доставить данные коллегам на их локальные машины. Плохой с точки зрения контроля качества данных, на основе которых принимаются решения. Подход имеет ряд ограничений, но есть и безусловное преимущество: поскольку масштабировать его невероятно сложно, а надёжность не сравнима ни с чем, поддержка таких решений, которые остались от предшественников, — явление редкое :)

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

ETL на коленке

В качестве примера простого ETL-pipelin-а напишем небольшой кусочек кода, в котором обратимся к внешнему сервису с открытыми данными, изменим их, отправим себе в любимый мессенджер и сохраним. В нашем примере, внешним сервисом будет интернет-банк, к которому можно обратиться по API, допустим, Mono. Мы будем получать (по утрам) курс валют, отправлять его себе в телеграм-канал и записывать данные в свою же гугл-табличку.

Попробуем реализовать это сначала на Python на своем компьютере. Если вы никогда ничего у себя локально на Python не запускали, стоит предварительно ознакомиться с инструкциями, как его установить, и добавить зависимости (библиотеки), которые нам в дальнейшем пригодятся. Для этого очень советую Flask tutorial. А весьма простой подход решать проблемы при разработке собственных программ — находить решения похожих проблем на Stack Overflow.

Шаг первый. Достанем и отобразим данные, которые наш банк готов отдать:

# импортируем необходимые библиотеки Python
import json
from urllib import parse, request

# Предоставленный банком адрес
URL = "https://api.monobank.ua/bank/currency"

# Выделим, расшифруем и выведем в консоль полученную информацию
content = request.urlopen(url=URL).read()
content_decoded = json.loads(content.decode("utf-8"))
print(content_decoded)

Если всё прошло успешно, в командной строке увидим список интуитивно непонятных записей. Та часть, которая нас интересует, выглядит примерно так:

{'currencyCodeA': 840, 'currencyCodeB': 980, 'rateBuy': 25.0, 'rateSell': 26.0}

Здесь находятся ISO-коды валют, стоимость покупки и продажи по состоянию на сегодня. Курс доллара к гривне идет первым элементом — с ним и будем работать дальше*.

*Доставать данные из списка по их порядковой позиции — плохая практика. Никто не гарантирует, что завтра первым элементом не будет другая валюта или другие данные. Хороший подход — превращать данные, например, в хеш-таблицу и доставать только интересующие вас записи (а если не вышло, явно отдавать ошибку).

Шаг второй. Отправим себе в телеграм-чат полученные данные. Здесь предварительно нужно создать нового телеграм-бота и получить его токен (инструкция). А также добыть Chat ID — идентификатор чата, в котором мы хотим видеть сообщения от бота*:

# Оставим в константах токен telegram бота и chat id
TOKEN = "bot111222333:ABGxsjr-Abracadabra_U5aALnmu-cCern"
CHAT_ID = 777888999

# Приведем нашу строку с курсом к дружелюбному виду
target_note = content_decoded[0]
message = f"Currency {target_note['currencyCodeA']} to " \
          f"{target_note['currencyCodeB']} rate: \n " \
          f"rate buy: {target_note['rateBuy']} \n " \
          f"rate sell: {target_note['rateSell']}"

# Сформируем запрос и отправим сообщение себе в чат
bot_url = f"https://api.telegram.org/{TOKEN}/sendMessage?» \    
          f«chat_id={CHAT_ID}&text={parse.quote(message)}&parse_mode=html"
request.urlopen(bot_url)

*Узнать Chat ID всех собеседников бота можно с помощью браузера, обратившись в адресной строке к https://api.telegram.org/<YourBotToken>/getUpdates.

Успешным результатом этого шага будет доставленное сообщение в телеграм-чат:

Шаг третий. Внесем полученные данные в Google Sheets. Для этого надо установить внешние зависимости и получить service account key из Google-консоли.

Внешние зависимости установим, выполнив в консоли:

pip3 install gspread oauth2client

Инструкцию по генерации ключа доступа к сервисам Google здесь приводить не буду — её можно найти в документации к установленной библиотеке gspread. Сам же код будет выглядеть следующим образом:

# Импорт установленных библиотек
import gspread
from oauth2client.service_account import ServiceAccountCredentials

# Объявим, с какими сервисами будем работать
scope = ['https://www.googleapis.com/auth/spreadsheets',
        'https://www.googleapis.com/auth/drive']

# Авторизуемся со своим Service Account Key
client = gspread.authorize(ServiceAccountCredentials.from_json_keyfile_name(
         'credentials.json', scope))

# Обозначим, к какому документу мы обращаемся
doc_name = 'test sheet'
sheet = client.open(doc_name)
worksheet = sheet.worksheet('Sheet1')

# И запишем данные в саму google-таблицу
worksheet.append_row(['Сurrency ISO', 'Сurrency ISO', 
                      'Buy rate', 'Sell rate'])
worksheet.append_row(
    [target_note['CurrencyCodeA'],
     target_note['CurrencyCodeB'],
     target_note['rateBuy'], 
     target_note['rateSell']])

Если всё прошло успешно, мы увидим вот такую запись в своей гугл-табличке:

Полностью весь скрипт можно найти на GitHub*.

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

Итак, что мы реализовали выше:

  • достали интересующие нас данные по API из стороннего сервиса (Extract);
  • отфильтровали и привели в читабельный вид (Transform);
  • отправили себе в чат и положили на хранение в табличку (Load).

Наш ETL-инструмент состоит исключительно из скрипта, который выполняет основную работу. Но в бизнесе хорошие технические решения часто содержат в себе не только core-фичу, которая выгружает, изменяет и сохраняет данные, но и несколько дополнительных полезных компонентов:

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

Сложно ли автоматизировать такой маленький воркер

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

Если работа сервиса важна и полезна — лучше перенести код на сервер и автоматизировать его запуск. С этим может помочь девопс, а если такового нет, то сидящий ближе других разработчик.

Технически автоматизация не требует много времени. Выполнение нашего кусочка кода по расписанию на Linux-сервере займёт одну строку в cron-файле, на сервере с Windows — одну запись в планировщике задач.

Нужна ли документация для простых решений

Если коротко, то очень нужна. Если вы написали 20 строк кода и запускаете его по утрам, то в своей Wiki/Confluence/внутреннем Stack Overflow стоит простым языком вкратце описать возможности сервиса, его ограничения и к кому обратиться, если нужно будет чего-то подправить. Кроме того, хорошо будет добавить небольшую заметку для будущих разработчиков, которые займутся поддержкой сервиса (они вам будут благодарны). Отличный вариант для этого — залить код в любую используемую в вашей компании систему контроля версий с комментариями или readme-файлом.

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

Какие решения можно автоматизировать ещё

Приведенный пример (работа с API, мессенджером и гугл-таблицей) не будет самым распространённым ETL-процессом. Такие процессы, скорее всего, будут связаны с базами данных и предусматривать изменения в них самих или выгрузку из них чего-либо. После этого полученную информацию нужно будет визуализировать и доставить такую визуализацию коллегам. Хорошими примерами (и задачами для автоматизации) могут быть:

  • задачи по агрегации данных в БД (формированию табличек с агрегированными метриками в нескольких разрезах, для того чтобы использовать их, а не таблицы с сырыми данными — очень экономит ресурсы);
  • перелив в БД информации из внешних источников (по API, используя native tools для облачных решений);
  • перенос данных между таблицами/базами;
  • выгрузка данных из базы в удобном виде в Google Sheets для визуализации;
  • алерты при резких изменениях любых метрик, которые можно посчитать в базе данных;
  • сообщения в чат по утрам о состоянии продукта;
  • выгрузка Tableau-отчётов в Slack/Telegram/любимый мессенджер/на почту шефу каждый час, день или неделю (да, и у систем визуализации есть свой API).

Несмотря на то, что мощности серверов (и своих, и арендованных) почти всегда чего-то стоят, маленький дополнительный воркер они могут вытащить без существенных дополнительных затрат. Потому стоимость таких простых решений (в тех случаях, когда они не отрабатывают часами, переливая миллионы строк) определяется стоимостью вашей part-time работы.

Summary

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

Насколько они должны быть просты:

  • джун сможет понять, нагуглить решение и поправить код;
  • если вы уйдете из компании через две недели, такое решение сможет проработать еще два года (пока условный фейсбук не обновит в очередной раз свой API);
  • после того как сервис поломается, алерты в ваш чат будут лететь, пока не почистят от кода сервер (потому что сервис уведомлений имеет запас прочности, как гаечный ключ).

Про другие (более взрослые) ETL-решения можно почитать у ребят из Google (про то, как сделать всё на свете на их ресурсах), в блоге Netflix и на «Хабре».

Буду рад комментариям и фидбэку :)

👍НравитсяПонравилось2
В избранноеВ избранном2
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Справжні панки експортують дані тільки в CSV...

Как программировать «без программирования», используя программирование, и ещё кучку сопутствующих монструозных библиотечек, которые надо вызубрить — и всё это чтобы получить плохой нечитаемый код.

Поздравляю, вы открыли для себя скрипты и макросы. Когда и JavaScript таким был, и за это мы его любили. И даже Java такой была — но у комьюнити хватило ума убить в ней старой дерьмище при эволюции в мощный язык с ещё более мощными наработками.

Открою секрет: Все языки программирования родились чтобы было легче программировать. Но в результате откровенного самодурства и бюрократизма (и необходимости поддержки легаси), затраты на IT разработку пробивают небесную твердь.

Подписаться на комментарии