Автоматизация работы с магазином 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», ничего не меняя.

Первый шаг подключения библиотеки к Скрипту
Первый шаг подключения библиотеки к Скрипту

Если вдруг вы не уверены в правильности совершённых ранее действий, рекомендую сделать копию этой таблицы — в ней уже всё подключено, настроено и даже вписан весь нужный код.


Скрипт для загрузки товаров

Давайте теперь разберёмся со структурой скрипта. Из иллюстраций к инструкции о подключении библиотеки к скрипту видно, что я разделил его на три файла: indextools и configs. В файле index будут размещены основные исполняемые функции, в tools — функции-помощники для обработки данных и рутинных действий, а в configs — константы с настройками.

Настройки

Позволю себе не рассказывать о том, как получить ключи для доступа к API. На эту тему доступно очень много материалов — уверен, это не составит для вас проблемы. Тем не менее это самый важный пункт настроек, без которого точно ничего не получится. Открываем файл configs и первым делом меняем ключи на свои.

Структура файлов скрипта и объект с ключами API
Структура файлов скрипта и объект с ключами API

Теперь нужно определить, куда и какие данные записывать.

Для того чтобы определить лист для записи данных о товарах, нужно вписать его название в переменную 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 предоставляет два метода. Первый отдаёт их идентификаторы, согласно переданным условиям, а второй — возвращает полный перечень информации о переданном списке идентификаторов.

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

Как видно на иллюстрации ниже, в результате наших действий мы получим массив объектов с информацией о товарах и уровнем вложенности полей не более одного. Это необходимо для корректного преобразования данных в табличный формат. Данные для передачи в качестве параметров запросов в точности соответствуют документации, поэтому трудностей с их изменением возникнуть не должно.

Функция для получения данных о товарах из Озон по API, их обработки и записи в таблицу
Функция для получения данных о товарах из Озон по API, их обработки и записи в таблицу

Запись данных о товарах в Google-таблицу

После того как данные успешно получены из Ozon и предварительно обработаны, остаётся только преобразовать их в необходимый формат (а именно — в массив строк) и записать в таблицу.

Преобразование и запись данных в таблицу после их загрузки по API выглядит следующим образом:

Преобразование и запись данных в таблицу после их загрузки из Озон по API
Преобразование и запись данных в таблицу после их загрузки из Озон по 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 может быть информативнее, чем название. Кроме того, это позволяет достаточно комфортно управлять товарами, например изменять их цены или остатки. Эти возможности мы рассмотрим в следующей статье.

Моё мнение по поводу информативности артикулов сильно изменилось, после того как я начал составлять названия товаров под ключевые запросы в категории. Теперь верю.)

Если у вас остались вопросы или что-то не получается, приходите в чатик «Таблицы и Скрипты Гугл», где мы помогаем разобраться с Таблицами и Скриптами.

А еще больше интересных интеграций, разбор реальных кейсов и хитрости при работе с Таблицами можно найти в нашем Телеграм-канале «Таблицы Гугл»

Комментарии (2)


  1. GrimAnEye
    21.06.2024 18:49

    Сомнительное предложение, относительно текущих реалий, выгружать данные в иностранные сервисы, которые могут быть заблокированы по постановлению США


    1. mityayka1 Автор
      21.06.2024 18:49
      +1

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

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