Я делаю приложение для кухни: что лежит дома, до какого числа, что купить и что из этого приготовить. Самая скучная его часть оказалась самой трудной: чтобы человек навёл камеру на пачку гречки и получил «Гречка ядрица, 900 г, 313 ккал», нужна база штрихкодов российских продуктов. Я был уверен, что она где-то есть. Её нет. Ниже — что я проверял, что из этого работает и как в итоге устроен справочник, в котором сейчас 258 тысяч позиций.
Где я искал и что нашёл
Проверял живыми запросами, а не по описаниям. Итог одной таблицей:
|
Источник |
Что там |
Годится |
|---|---|---|
|
Open Food Facts, российская часть |
35 тысяч товаров, КБЖУ примерно у трёх из четырёх в выборке |
да, но это в основном крупные бренды |
|
Открытая база с Хабра (1,8 млн кодов, CSV на 244 МБ) |
категория, бренд, название, артикул |
нет: автор исключил продукты, лекарства, одежду и обувь намеренно |
|
Национальный каталог «Честного знака» |
вся маркированная молочка и вода |
нет: API только по договору интегратора |
|
GS1 Russia / GEPIR |
владелец префикса |
нет: отдаёт компанию, а не товар |
|
opengtindb.org |
немецкий рынок |
нет: регистрация, русских товаров нет |
|
barcode-list.ru |
поиск по коду и по названию |
да, и это главная находка |
С barcode-list.ru история такая: сайт отвечает по коду за 0,4 секунды, robots.txt разрешает обход целиком с Crawl-delay: 10, а на один код отдаёт несколько вариантов названия с рейтингом. Выкачивать его целиком я не стал — незачем. Вместо этого база собирается ленивым догоном: человек сканирует код, которого у меня нет, промах уходит в очередь, воркер спрашивает barcode-list с паузой в десять секунд и кладёт ответ в кэш. Второй человек с той же пачкой получает название уже из моей базы. Никаких выгрузок и договоров: база растёт ровно на то, что люди сканируют.
Отдельная строка — магазины. Я хотел взять каталоги сетей, где люди реально покупают: там и штрихкод, и название, и часто КБЖУ. Ашан и Лента за Qrator, Самокат, Пятёрочка и Чижик за ServicePipe с JS-челленджем на первом же запросе, у «Марии-Ра» онлайн-каталога нет вовсе. Открытыми оказались четыре каталога — METRO, Магнит, Быстроном и Добрянка, и только у METRO есть и штрихкод, и КБЖУ в одной карточке. Из них собрано ядро: 258 тысяч позиций, у 242 тысяч есть EAN-13, КБЖУ заполнены у 13 тысяч. Остальное дозаполняется по мере сканов.
Чек вместо каталога
Заметная часть покупок у людей вокруг меня приезжает из Самоката, а штрихкодов у его собственной фасовки нет. Зато есть чек. Электронный чек Платформы ОФД открывается по ссылке без авторизации и содержит полный состав покупки — ровно то, что я не смог забрать из каталога.
Устроен он занятно: состав лежит в скрытом div экранированным HTML, после раскодирования это лента из 182 вложенных таблиц. JSON-эндпоинта нет, QR с бумажного чека требует авторизации в ФНС, так что единственный вход — ссылка на электронный чек. Парсер режет ленту на позиции, нормализатор названий (тот же, что чистит каталоги магазинов) сопоставляет «Йогурт натур. 3,2% 130г» с типом продукта из справочника, и человек видит экран подтверждения: что опознано, что нет, что добавить в запасы. Живой прогон на настоящем чеке из 21 позиции дал 14 записей в запасах: три йогурта (130, 140 и 140 г) слились в один на 410 г.
Краудсорсинг без личных данных
Когда сканер промахивается мимо всех источников, приложение просит человека описать продукт: тип из справочника, вес, срок. Раньше эта пачка оставалась безымянной строкой, теперь описание уезжает на сервер парой «код → тип» — без имени, без времени, без состава семьи. Пара попадает в модерацию, и только после одобрения становится общей. Модерация — Laravel и Filament поверх того же Postgres, но решение принимает не PHP, а хранимая процедура: одна транзакция на одобрение, золотую запись и лог. Правил модерации в админке нет намеренно, иначе они разъедутся с мобильным клиентом.
Сколько это даёт? Честно — пока мало: 135 сканов от 10 устройств. База штрихкодов — актив на годы, а не на месяц, и она не нужна человеку, который ведёт только список покупок. Об этом ниже.
Офлайн-first, и почему это было обязательно
Холодильник стоит на кухне, а кухня — часто худшее место для Wi-Fi в квартире. Поэтому приложение читает только локальную базу (drift поверх SQLite), а каждая запись идёт в две таблицы: в локальную и в очередь операций. Движок синка реплеит очередь батчами в Supabase и подтягивает чужие изменения; конфликты решаются по updated_at, последний пишет.
Две грабли, на которые стоило наступить один раз:
-
Сетевую ошибку и отказ сервера нельзя лечить одинаково. У очереди есть карантин: если сервер отверг операцию (RLS, валидация), счётчик попыток растёт и после порога операция уходит в dead-letter с кнопкой «Повторить». Транзиентная ошибка сети счётчик не трогает. Пока это было одним путём, валидные офлайн-правки улетали в карантин из-за лифта.
-
Клиент слал локальное время без суффикса
Z, Postgres читал его как UTC. Устройство в UTC+7 всегда выигрывало у остальных при слиянии, устройство западнее Гринвича всегда проигрывало. Правится одной строкойtoUtc(), но данные на бою пришлось выравнивать руками, а метки «из будущего» лезли ещё неделю — с телефонов, которые не обновились.
Supabase self-hosted на своём VPS: Postgres, PostgREST, GoTrue, realtime. Схемой владеют только миграции, RLS на каждой таблице, аккаунт создаётся анонимно при первом запуске — регистрации нет, потому что для списка покупок она не нужна.
Аналитика, которая не знает, что вы едите
Продуктовые события уезжают в AppMetrica, но словарь событий закрытый: item_added, scan_finished, shopping_list_closed. Параметры — только из перечислений, свободный текст в событие не собрать физически: конструктор события приватный, фабрики типизированы, тест проверяет, что ни одно значение не выходит за словарь. Названия продуктов, аллергены, диеты, имена — не уходят никуда. Идентификатор установки не связывается с аккаунтом. Это не благотворительность, а 152-ФЗ и здравый смысл: что лежит у человека в холодильнике, мне знать незачем.
Что дальше
Приложение бесплатное, без рекламы и покупок, стоит в RuStore, в Google Play идёт закрытый тест, App Store впереди. Код закрыт, но подход к базе открыт здесь целиком — повторить можно за вечер. Если у вас есть источник российских продуктов со штрихкодами, которого нет в таблице выше, — напишите, я проверю живым запросом и допишу.
Сайт: https://chembogaty.ru
ссылка на оригинал статьи https://habr.com/ru/articles/1082940/