Привет, я Настя — младший разработчик в M2. М2 — это экосистема цифровых сервисов, которые помогают пользователям проводить сделки с недвижимостью удобно, быстро и безопасно. Мы с командой разрабатываем CMS, которая позволяет управлять контентом на сайте m2.ru
Расскажу о том, как мы используем Apps Script для парсинга данных из Google Sheets и зачем это нам. Если вам интересна только пошаговая инструкция с примерами кода, ее можно посмотреть здесь.
Где это использовать?
Если перед вами стоит похожая задача, о которой я расскажу дальше, когда нужно быстрое и простое решение, которое сэкономит кучу часов рабочего времени вас и ваших коллег.
В пет‑проектах, когда нет бэкенда, но нужно соорудить что‑то типа базы данных.
А еще можно использовать в своих собственных проектах, например, мой коллега вдохновился и решил накопить на квартиру с помощью Google Sheets + Apps Script. В этой статье он делится своими наработками.
Как мы докатились до жизни такой
В нашем сервисе есть раздел саппорта, где сотрудники могут поддерживать соответствующий раздел на сайте. Они анализируют популярные вопросы от пользователей, формируют ответы и инструкции, а затем выкладывают эту информацию на сайт.
Честно говоря, загружать контент на сайт было утомительным занятием. Сначала сотрудники саппорта собирали все ответы в Google Sheets, потому что в таком формате им удобно хранить и редактировать данные. Затем им нужно было внести все те же самые данные в CMS. Автоматической интеграции к тому моменту не было, поэтому им приходилось копировать и вставлять каждый вопрос/ответ вручную. И, наконец, уже оттуда опубликовать на сайт. Эта рутинная работа занимала много времени, поэтому коллеги обратились к нам с задачей облегчить этот процесс. Тем более, впереди нас ожидало глобальное обновление всего раздела саппорта, а, значит, мы рисковали на много часов потерять коллег.
Зачем вообще нужна такая прослойка в виде CMS и почему бы не загружать напрямую из Google Sheets? Во‑первых, это невозможно технически, а, во‑вторых, в целях безопасности, так как все данные сохраняются у нас в базе, и если однажды Google совсем не будет доступен в России, то у нас ничего не сломается.
После небольших раздумий нам пришла в голову идея: если ребятам удобно работать в Google Sheets, то мы можем использовать ее, как некую базу данных и написать скрипт, который будет парсить все эти данные и выгружать в наш сервис нажатием на одну кнопку. Таким образом, мы сделаем их работу более удобной и быстрой. А еще эта идея была самой простой и бюджетной в реализации.
На помощь нам пришел Apps Script - платформа на основе JavaScript, которая дает возможность быстро разрабатывать приложения, позволяющие автоматизировать, настраивать и расширять возможности работы с Google Sheets и не только с ними.
Этот вариант подошел нам больше всего, так как Google Sheets были исходными данными, а там есть только Apps Script. В качестве альтернативы можно было бы рассмотреть реализацию своих «гугл‑таблиц» или поднятие бекэнда для яндекс-таблиц, но мы искали быстрое и недорогое решение.
В этой статье я не буду углубляться в то, как эта штука работает и как много она умеет, а поделюсь конкретной инструкцией, как мы используем ее для сбора данных из таблицы и преобразуем их в JSON.
Инструкция — как использовать Google Sheets в качестве базы данных
Шаг 1
Заходим в таблицу, с которой мы будем работать. В меню выбираем пункт Расширения → Apps Script.
Шаг 2
В открывшемся редакторе нам нужно написать конфиг и несколько функций, которые будут парсить таблицу. Начнем с конфига. Создаем файл config.gs и в нем задаем основные данные, которые будут учитываться скриптом.
/*
spreadsheetId - id таблицы
columnMapping - маппинг названий колонок (колонки, которые тут не перечислены, не будут включены в json)
dropdownMappings - маппинг раскрывающихся списков (при необходимости), чтобы сразу же использовать их без последующей конвертации
spreadSheetMapping - маппинг названий листов (листы, которые тут не перечислены, не будут включены в json)
startRow - указываем номер строки, с которой начинается таблица (включая строку с заголовками)
*/
let config = {
spreadsheetId: '',
columnMapping: {},
dropdownMappings: {},
sheetsMapping: {},
startRow: 2,
};
Соответственно, в данном конфиге ключ — это название сущности в таблице, а значение - то, как мы хотим это видеть в JSON.
Шаг 3
Далее приступаем к написанию функций. Первая функция convertRowToJson преобразует каждую строку в таблице в объект JSON:
function convertRowToJson(row, headers, config, sheetName) {
const record = {};
headers.forEach((header, index) => {
if (config.columnMapping.hasOwnProperty(header)) {
const value = row[index];
const mappedHeader = config.columnMapping[header];
if (config.dropdownMappings.hasOwnProperty(header) && config.dropdownMappings[header].hasOwnProperty(value)) {
record[mappedHeader] = config.dropdownMappings[header][value];
} else {
record[mappedHeader] = value;
}
}
});
record.sheetName = sheetName;
return record;
}
Шаг 4
Следующая функция convertRangeToJson преобразовывает диапазон данных из таблицы в массив объектов JSON, используя config и имя листа. Здесь же мы используем нашу предыдущую функцию convertRowToJson:
function convertRangeToJson(data, config, sheetName) {
const jsonArray = [];
if (!data || data.length < config.startRow) {
return jsonArray;
}
const headers = data[config.startRow - 1];
data.slice(config.startRow).forEach(row => {
const record = convertRowToJson(row, headers, config, sheetName);
jsonArray.push(record);
});
return jsonArray;
}
Шаг 5
Функция processSheet обрабатывает данные с каждого листа, указанного в конфиге:
function processSheet(sheet, config) {
const sheetName = sheet.getName();
if (!config.sheetsMapping.hasOwnProperty(sheetName)) {
return [];
}
const mappedSheetName = config.sheetsMapping[sheetName];
const data = sheet.getDataRange().getValues();
if (data.length < config.startRow) {
return [];
}
return convertRangeToJson(data, config, mappedSheetName);
}
Шаг 6
И, наконец, последняя функция doGet, которая позволяет создать простое REST API для доступа к данным Google Sheets и используется для обработки HTTP GET-запросов:
function doGet(req) {
const spreadsheet = SpreadsheetApp.openById(config.spreadsheetId);
const sheets = spreadsheet.getSheets();
const allData = sheets.reduce((acc, sheet) => {
const jsonData = processSheet(sheet, config);
return acc.concat(jsonData);
}, []);
return ContentService.createTextOutput(JSON.stringify(allData))
.setMimeType(ContentService.MimeType.JSON);
}
Шаг 7
Теперь остается сохранить изменения и развернуть проект. После сохранения нажимаем на «Начать развертывание» → «Новое развертывание». В открывшемся окне заполняем все поля, как на скрине (тип — веб‑приложение, запуск от моего имени, у кого есть доступ — все) и нажимаем на «Начать развертывание»:
Шаг 8
При нажатии на кнопку «Начать развертывание» у вас может открыться окно с требованием разрешить доступ к данным. В таком случае нужно нажать на Предоставить доступ, выбрать свой аккаунт в Google и… вы увидите такую страницу:
Чтобы все поправить, нужно нажать на «Advanced» в левом нижнем углу и в открывшемся блоке нажать на ссылку перехода к своему проекту. После этого развертывание завершится и вы получите url, к которому можно делать запросы. Можно нажать на него и увидеть, что получилось:
Если вам нужно что-то поменять в скрипте, например, поправить конфиг, то после сохранения внесенных изменений:
нажмите на «Начать развертывание» → «Управление развертываниями».
В открывшемся окне выберите свой проект, нажмите на иконку редактирования и в поле «Версия» выберите «Новая версия».
После этого нажмите на «Начать развертывание».
Шаг 9
Затем можно делать с полученным JSON все, что угодно. Например, мы написали на бэкенде небольшой сервис, который обращается по указанному эндпоинту и сохраняет полученные результаты в базе. После этого мы уже работаем с этими данными для публикации контента на сайте.
И что в итоге получилось?
Таким образом, алгоритм работы сотрудника отдела саппорта стал гораздо проще: он заполняет свою таблицу → заходит в наш сервис → нажимает кнопку «Импортировать» → проверяет загруженные данные → публикует на сайт.
Получается, что мы сократили примерно в 2 раза работу по загрузке вопросов на сайт, так как теперь нет необходимости дублировать контент, сделали этот процесс более удобным, интуитивно понятным и гибким. При этом мы потратили совсем немного времени на реализацию такой идеи.
А как же безопасность?
Да, конечно, этот способ не заменит полноценный бэкенд и не подойдет для хранения конфиденциальной информации. И, вообще, может произойти все, что угодно, так что к этому нужно относиться осторожно.
Заключение
В этой статье я поделилась пошаговой инструкцией использования Google Sheets в качестве подобия базы данных. Код можно скопировать полностью, настроить конфиг под себя и использовать. Надеюсь, что вам это поможет легко и быстро внедрить такую схему в свой проект.
Расскажите, используете ли вы Apps Script в своих проектах? Какие проблемы решали с помощью такого инструмента? А может быть вы столкнулись со сложностями, тоже интересно было бы об этом узнать.
Комментарии (13)
Xius
24.09.2024 13:21которое сэкономит кучу часов рабочего времени вас и ваших коллег
Лишь бы не стало это решение в дальнейшем генератором негативных эмоций у клиентов. В одной организации наблюдал внутренний софт с базой на Sheets. Задумчивый - это мягко сказано.
AnaSergeeva Автор
24.09.2024 13:21Ого) не, мы если и планируем где-то использовать такой подход, то только для таких точечных решений.
а почему негативные эмоции у клиентов?
natexriver
24.09.2024 13:21Из-за лимитов не получится хоть сколько нибудь интенсивно использовать Sheets API. Хотя я тоже как-то использовал связку скрипта и таблиц, для промокодов (запросов немного).
AnaSergeeva Автор
24.09.2024 13:21Да, согласна. В нашем кейсе тоже не так много запросов, поэтому такая схема подошла и уже несколько месяцев работает
vojaganto
24.09.2024 13:21Наверняка есть какой-то опен сорс self hosted аналог google spreadsheets. Вот его-то и надо было использовать.
AnaSergeeva Автор
24.09.2024 13:21Возможно есть, честно сказать, мы в эту сторону не копали :( У нас было дано - есть гугл таблица, которую редактируют коллеги, и грубо говоря надо оттуда достать все данные и выгрузить на сайт. Самое очевидное как раз было использовать готовые инструменты от гугла
SidVisceos
24.09.2024 13:21Извините, но таблицы (гугловские, ексель,...) НЕЛЬЗЯ называть базой данных. Где внешние ключи в таблицах, где ограничения, где связи "один ко многим" например,.....?
Ваше решение тоже имеет место быть, но похоже на "мы создали проблему и героически её решили".
Не проще ли купить бомж хостинг за 100 рублей, запустить там SQLite (если это действительно бомж хостинг и нет MySQL)?
Можно даже на своём компе запустить SQL сервер и прокинуть его в интернет с помощью какого нибудь Tuna или xTunnel,.... Даже это мне кажется будет лучше чем таблицы гугла.
AnaSergeeva Автор
24.09.2024 13:21Абсолютно согласна) В нашем случае мы и не используем таблицы как базу данных в классическом смысле. Гугл-таблицы тут — это просто удобный интерфейс для работы с данными, который используют наши коллеги. В данном случае мы забираем данные из таблиц, трансформируем их в нужный формат и сохраняем в MongoDB, где уже обеспечены связи, схемы данных и оптимизированные запросы.
Базы данных в названии - это конечно очень грубо говоря и не в прямом смысле, про это я уточняю в статье
beoserg
24.09.2024 13:21+1Нельзя назвать реляционной базой данных, но базой данных назвать можно, так как она хранит данные.
BasiC2k
Было бы неплохо упоминать про Google quotes в таких статьях. А именно, что количество запросов и общее время выполнения скриптов - ограничено.
Кроме того, выполнение GET вызова, запрос данных с помощью
sheet.getDataRange().getValues()
например на 20000 строк и последующая обработка занимает 1-2 секунды, что может привести к коллизиям даже в среднезагружнных проектах.
AnaSergeeva Автор
Спасибо за комментарий! Да, действительно, ограничение такое есть, это важное замечание.
А по второму пункту, какой вариант тут можно предложить для оптимизации?
BasiC2k
По вопросу устранения коллизий я так и не нашёл другого решения, кроме как - не использовать spreadsheets для средненагруженных и важгых проектов.Поэтому сам с интересом слежу за статьями о Google spreadsheets c надеждой, что эта проблема когда-нибудь будет решена.
DevFx
"quotes" --> "quotas"