Автоматизация работы с магазином Ozon с помощью Google-таблиц и Ozon Seller API.
Всем привет! Меня зовут Дмитрий, и я, вместе с командой GoogleSheets.ru, занимаюсь аналитикой и автоматизацией бизнеса и его интеграцией во внешние системы. Для этих целей мы разрабатываем различные веб-сервисы и приложения, но иногда, когда это рационально, используем Google-таблицы и скрипты Google Apps Script (дальше я их буду называть просто скриптами).
Я веду рубрику "Ozon и Google-таблицы: продаём товары «в клеточках»" вместе с площадкой Озон для разработчиков, где рассказываю о способах интеграции Google-таблиц с магазином Ozon с помощью Ozon Seller API и хотел бы поделиться этим материалом с аудиторией Хабра.
Мой опыт автоматизации бизнеса говорит о том, что Google-таблицами для решения тех или иных задач, связанных с работой на маркетплейсах, не пользуются две категории продавцов. Первая категория — это по-настоящему большой бизнес, использующий специальные и дорогие решения, а вторая — те, кто про этот инструмент по каким-то причинам ещё не знают. Согласитесь, большинство продавцов в эти категории не попадает.
Когда-то давно я уже публиковал решение и библиотеку для переноса цен из Google-таблицы, но с того времени возможности Ozon Seller API сильно расширились, да и методы изменились. Поэтому, несмотря на то что старая версия API ещё работает, библиотека уже не является актуальной, а реализация методов текущей версии API применительно к скриптам требует отдельного рассмотрения и демонстрации.
Я собираюсь в нескольких статьях раскрыть реализацию методов актуального Ozon Seller API применительно к Google-таблицам и продемонстрировать их практическое применение в интересах продавца. А начну я с ответа на самый частый вопрос, который мне задают: «Как выгрузить товары из Ozon в Google-таблицу?».
Библиотека
Давайте начнём с библиотеки. Сейчас она представляет собой клиент для отправки запросов, адаптированный для выполнения в скриптах, и всего несколько методов, которые будут использоваться при переносе списка товаров из Ozon в таблицу. С каждой последующей темой она будет дополняться и обновляться.
Библиотека опубликована на GitHub, где её можно рассмотреть более подробно, а все методы и типы в ней полностью соответствуют официальной документации Ozon Seller API. Чтобы можно было поставить себе галочку за соблюдение принципа разделения ответственности, упростить поддержку и исключить путаницу, ничего, кроме того, что есть в документации, в этой библиотеке нет. Различные функции-помощники, синтаксический сахар и вообще всё, что не касается методов Ozon Seller API, будет в статьях и таблице-примере.
Инструкция как подключить библиотеку к скрипту ниже
Итак, библиотека подключена. Теперь нужно подключить службу Sheets, потому что для работы с таблицей мы будем использовать Google Sheets API, что существенно ускорит выполнение скрипта, особенно в операциях записи данных. Так же подробно инструкцию я расписывать не буду, так как она гораздо проще. Нужно нажать на иконку «+» напротив раздела «Сервисы» и выбрать «Google Sheets API», ничего не меняя.
Если вдруг вы не уверены в правильности совершённых ранее действий, рекомендую сделать копию этой таблицы — в ней уже всё подключено, настроено и даже вписан весь нужный код.
Скрипт для загрузки товаров
Давайте теперь разберёмся со структурой скрипта. Из иллюстраций к инструкции о подключении библиотеки к скрипту видно, что я разделил его на три файла: index, tools и configs. В файле index будут размещены основные исполняемые функции, в tools — функции-помощники для обработки данных и рутинных действий, а в configs — константы с настройками.
Настройки
Позволю себе не рассказывать о том, как получить ключи для доступа к API. На эту тему доступно очень много материалов — уверен, это не составит для вас проблемы. Тем не менее это самый важный пункт настроек, без которого точно ничего не получится. Открываем файл configs и первым делом меняем ключи на свои.
Теперь нужно определить, куда и какие данные записывать.
Для того чтобы определить лист для записи данных о товарах, нужно вписать его название в переменную ProductsSheetName. Обязательно убедитесь, что лист с таким названием есть в таблице, иначе получите ошибку при выполнении скрипта.
Далее в том же файле в качестве переменной fieldsToShow определён массив с перечнем всех полей, которые возвращаются API в объекте товара, и их описаниями.
Код здесь
/**Перечень и описание всех полей товара
* Если какое-то из них не нужно в таблице,
* поле достаточно просто закомментировать ("//" в начале строки)
* или наоборот, убрать "//" из начала строки, чтобы это полу выводилось в таблицу
*/
const fieldsToShow = [
'barcode' /** Штрихкод. */,
// 'barcodes', /** Все штрихкоды товара. */
'buybox_price' /** Цена главного предложения на Ozon. */,
// 'category_id', /** Идентификатор категории. */
// 'color_image', /** Маркетинговый цвет. */
'created_at' /** Дата и время создания товара. */,
'fbo_sku' /** SKU товара, который продаётся со склада Ozon (FBO).*/,
'fbs_sku' /** SKU товара, который продаётся со склада продавца (FBS и rFBS).*/,
// 'id', /** Номер задания на формирование документов. */
/** Массив ссылок на изображения. Изображения в массиве расположены в порядке их расположения на сайте. Если параметр
* `primary_image` не указан, первое изображение в массиве главное для товара.
*/
// 'images',
'primary_image' /** Главное изображение товара. */,
// 'images360', /** Массив изображений 360. */
'has_discounted_item' /** Признак, что у товара есть уценённые аналоги на складе Ozon. */,
/**
* Признак, является ли товар уценённым:
* - Если товар создавался продавцом как уценённый — `true`.
* - Если товар не уценённый или был уценён Ozon — `false`.
*/
'is_discounted',
'discounted_stocks' /** Остатки уценённого товара на складе Ozon. */,
'is_kgt' /** Признак крупногабаритного товара. */,
/**
* Валюта ваших цен. Cовпадает с валютой, которая установлена в настройках личного кабинета.
* Возможные значения:
* - `RUB` — российский рубль,
* - `BYN` — белорусский рубль,
* - `KZT` — тенге,
* - `EUR` — евро,
* - `USD` — доллар США,
* - `CNY` — юань.
*/
'currency_code',
'marketing_price' /** Цена на товар с учётом всех акций. Это значение будет указано на витрине Ozon. */,
'min_ozon_price' /** Минимальная цена на аналогичный товар на Ozon. */,
'min_price' /** Минимальная цена товара после применения акций. */,
'name' /** Название. */,
'offer_id' /** Идентификатор товара в системе продавца — артикул. */,
'old_price' /** Цена до учёта скидок. На карточке товара отображается зачёркнутой. */,
'premium_price' /** Цена для клиентов с подпиской [Ozon Premium](https://docs.ozon.ru/common/ozon-premium/). */,
'price' /** Цена товара с учётом скидок — это значение показывается на карточке товара. */,
/** Ценовой индекс. Подробнее в [Базе знаний продавца]
* (https://seller-edu.ozon.ru/docs/kontrol-kachestva/kak-my-kontroliruem-kachestvo-raboty-partnera.html#индекс-цен/).
*/
'price_index',
'recommended_price' /** Цена на товар, рекомендованная системой на основании схожих предложений. */,
'status' /** Описание состояния товара. */,
// 'sources', /** Информация об источниках схожих предложений. */
'stocks' /** Информация об остатках товара. */,
'updated_at' /** Дата последнего обновления товара. */,
// 'vat', /** Ставка НДС для товара. */
'visibility_details' /** Настройки видимости товара. */,
'visible' /** Если товар выставлен на продажу — `true`. */,
];
Вы можете сами решить, какие из данных, перечисленных в этом массиве, вам нужно отображать в таблице. Для того чтобы исключить поле, достаточно его просто закомментировать. Обратите внимание, что данные, которые передаются по API от Ozon со временем могут измениться, поэтому при появлении новых полей в документации, их нужно будет внести и в скрипт.
И заключительный пункт настройки — настройка преобразования объектов с данными о товаре.
Код здесь
/** Настройки для обработки данных, полученных из Озон */
const flatConfig = {
/** Как форматировать даты в ответе "date" - 29.03.2023; "dateTime" - 29.03.2023 15:08 */
formatDate: 'dateTime',
/** Список полей, которые нужно вывести в ответе, если не указан - выводятся
* все поля, за исключением указанных в списке excludeFields
*/
onlyFields: fieldsToShow,
/** Список полей, которые исключить из ответа. */
excludeFields: [],
/**Список полей, которые нужно оставить без изменений и преобразований */
noFormatFields: ['offer_id'],
};
Эти настройки находятся в объекте flatConfig. Как раз в него передаются поля для вывода в таблице (свойство onlyFields). Также можно определить поля, которые нужно исключить из отображения в таблице, и те, которые нужно оставить без форматирования. Например, артикул 001234 в таблице будет числом 1234, потому что под капотом скриптов крутится JavaScript, известный своим коварством в подобных случаях. Именно поэтому в списке уже есть поле offer_id.
Ещё один элемент настройки — способ преобразования даты из исходного состояния в формат, понятный Google-таблицам. Тут можно определить, как значения даты и времени будут отображаться в таблице.
Получение данных о товарах из Ozon
С настройкой закончили. Теперь можно загружать данные о товарах.
Для того чтобы получить информацию о товарах API предоставляет два метода. Первый отдаёт их идентификаторы, согласно переданным условиям, а второй — возвращает полный перечень информации о переданном списке идентификаторов.
Благодаря подключённой библиотеке получение полных данных о товарах требует совершения всего четырёх действий. При этом одно из них — это инициализация самого клиента, а второе — пересборка данных из результатов одного запроса для передачи в другой.
Как видно на иллюстрации ниже, в результате наших действий мы получим массив объектов с информацией о товарах и уровнем вложенности полей не более одного. Это необходимо для корректного преобразования данных в табличный формат. Данные для передачи в качестве параметров запросов в точности соответствуют документации, поэтому трудностей с их изменением возникнуть не должно.
Запись данных о товарах в Google-таблицу
После того как данные успешно получены из Ozon и предварительно обработаны, остаётся только преобразовать их в необходимый формат (а именно — в массив строк) и записать в таблицу.
Преобразование и запись данных в таблицу после их загрузки по API выглядит следующим образом:
Код получения и записи данных здесь
function loadAndWriteProductsInfo() {
/** Инициализируем апи клиент */
const client = OzonApi.client(keys);
const productsIdsListResponse = client.productApiGetProductList({
filter: {
/* Можно отфильтровать по offer_id, чтобы получить другие данные о товаре */
// offer_id: [''],
/* или по product_id */
// product_id: [''],
/** Здесь фильтр "по видимости", подробнее - https://docs.ozon.ru/api/seller/#operation/ProductAPI_GetProductList */
visibility: 'ALL',
},
limit: 20,
});
/** Пересобираем ответ со списком товаров в список offer_id */
const productsOfferIds = productsIdsListResponse.result.items.map((item) => item.offer_id);
/**Запрашиваем информацию о товарах по полученному списку offer_id */
const productsInfoListResponse = client.productApiGetProductInfoListV2({
offer_id: productsOfferIds,
});
/**
* Достаем объекты с информацией о товарах из ответа Озон
* и "сплющиваем" объекты в соответствии с настройками "flatConfig"
*/
const productsFlatCollection = productsInfoListResponse.result.items
.map(product => flatter(product, flatConfig));
/**
* Пересобираем данные в формат, подходящий для записи в таблицу
* (массив массивов, где каждый вложенный массив - строка)
*/
const productsInfoGrid = collectionToGrid(productsFlatCollection, { includeHeader: true });
/**Записываем данные в таблицу на лист указанный в настройках с помощью Sheets Api */
writeGridToTable(productsInfoGrid, productsSheetName);
showMsg();
}
Функция для записи в таблицу вынесена в файл tools и выглядит так:
Функция записи данных в Таблицу Гугл
/**
* Очищает и затем записывает данные в указанный лист таблицы
* @param {Array<Array<string|number|undefined>>} grid - массив массивов(строк) с данными для записи
* @param {string} sheetName - Имя листа, в который данные будут записываться
*/
function writeGridToTable(grid, sheetName) {
const table = SpreadsheetApp.getActive();
const tableId = table.getId();
const sheetForWrite = table.getSheetByName(sheetName);
const clearResult = Sheets.Spreadsheets.Values.clear(
{},
tableId,
buildA1String(1, 1, 90, 1000000, sheetName),
);
const rangeForWrite = buildA1String(1, 1, grid[0].length, grid.length, sheetName);
const writeResult = Sheets.Spreadsheets.Values.append(
{
majorDimension: 'ROWS',
range: rangeForWrite,
values: grid,
},
tableId,
rangeForWrite,
{
valueInputOption: 'USER_ENTERED',
},
);
table.toast('Данные успешно записаны', 'GoogleSheets.ru');
return true;
}
И вообще рекомендую заглянуть в файл tools — там ещё много интересного.
Заключение
В результате всех действий мы имеем возможность в два клика получить нужные и актуальные данные о товарах в формате таблицы.
Я постарался максимально доступно не просто изложить инструкцию, а рассказать о самом решении, не останавливаясь при этом на разборе каждой строчки кода.
Получение списка товаров — первый шаг к тому, чтобы иметь возможность соотнести другую информацию с конкретным товаром, например информацию о заказах, остатках или комиссиях. Не верю я, что для продавца артикул товара или его идентификатор в Ozon может быть информативнее, чем название. Кроме того, это позволяет достаточно комфортно управлять товарами, например изменять их цены или остатки. Эти возможности мы рассмотрим в следующей статье.
Моё мнение по поводу информативности артикулов сильно изменилось, после того как я начал составлять названия товаров под ключевые запросы в категории. Теперь верю.)
Если у вас остались вопросы или что-то не получается, приходите в чатик «Таблицы и Скрипты Гугл», где мы помогаем разобраться с Таблицами и Скриптами.
А еще больше интересных интеграций, разбор реальных кейсов и хитрости при работе с Таблицами можно найти в нашем Телеграм-канале «Таблицы Гугл»
GrimAnEye
Сомнительное предложение, относительно текущих реалий, выгружать данные в иностранные сервисы, которые могут быть заблокированы по постановлению США
mityayka1 Автор
Возможно. В принципе, любой сервис не находящийся под личным контролем можно подвергнуть сомнению.
Но хочу заметить, что даже если Таблицы в этом кейсе в один день заблокируют, то с точки зрения бизнеса потери будут минимальными, потому что сами данные хранятся в другом сервисе под названием Озон.