Собираем открытые данные с GitHub Actions

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

В этой заметке я расскажу:

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

В результате получится идеальный инструмент для сбора и публикации открытых данных.

Будем работать с API станций метро, которое предоставляет HeadHunter. Я буду приводить фрагменты конфига для GitHub Actions, а в конце дам ссылку на готовый репозиторий. Поехали!

1. Собрать данные из API

Получаем сырой JSON от API, красиво форматируем, сохраняем в файл:

- name: Fetch latest data
  run: |-
            curl https://api.hh.ru/metro | jq --indent 4 . > data/metro.json

2. Опубликовать датасет на гитхабе

Коммитим от имени специального пользователя actions@users.noreply.github.com — его можно использовать в сценариях GitHub Actions:

- name: Commit and push if changed
  run: |-
      git config user.name "Automated"
      git config user.email "actions@users.noreply.github.com"
      git add -A
      timestamp=$(date --rfc-3339=seconds --utc)
      git commit -m "Latest data: ${timestamp}" || exit 0
      git push      

Бонусом получаем автоматическую проверку, изменились ли данные с предыдущего запуска. Если не изменились, команда git add -A ничего не добавит, и коммит не произойдет. Таким образом, новая версия датасета опубликуется, только если есть изменения.

3. Автоматически актуализировать

Сценарий запускается по расписанию, как указано в cron-выражении. Здесь — в 23:00 UTC каждый день:

on:
    push:
    workflow_dispatch:
    schedule:
        - cron: "00 23 * * *"

4. Бонус: JSON → CSV

JSON — это замечательно, но неплохо бы публиковать датасет еще и в CSV. В этом поможет SQLite, который умеет нативно работать с JSON.

Создаем таблицу:

create table city (
    id text,
    name text,
    url text,
    fullkey
);

Загружаем данные из JSON:

insert into city (id, name, url, fullkey)
select
   json_extract(value, '$.id') as id,
   json_extract(value, '$.name') as name,
   json_extract(value, '$.url') as url,
   fullkey
from json_tree(readfile('data/metro.json'))
where path = '$' and type = 'object';

Выгружаем в CSV:

.mode csv
.headers on
.once data/city.csv
select id, name from city;

Сохраняем команды в файл и запускаем в сценарии:

- name: Convert json to csv
  run: |-
            sqlite3 -batch data/metro.db < json-to-csv.sql

⌘ ⌘ ⌘

Набор данных в трех форматах (JSON, CSV, SQLite) — готов! А теперь попробуйте на своем датасете ツ

Полный сценарий

Репозиторий с данными

Подписывайтесь на канал, чтобы не пропустить новые заметки 🚀