Для начала представлюсь. Меня зовут Евгений, я работаю в компании OpticsTrade, должность IT-разнорабочий. Компания занимается продажей оптических приборов с 1992 года, а последние несколько лет делает упор на ночную оптику и в частности - тепловизионные прицелы и приборы. По мере роста бизнеса и расширения ассортимента, компания столкнулась с проблемой остатков товаров и актуальными ценами. Если в начале моей работы, количество товара на сайте было в районе 3 тысяч, то на текущий момент позиций более 15 тысяч. Обновлять руками такое количество позиций нереально.
Об о всем по порядку
С чем предстоит работать:
Сайт на OpenCart;
Расширение АОП (Автоматическая обработка прайс-листов);
Поставщики - более 20;
Товары - более 15 тысяч.
Начальство любит экспериментировать и проверять гипотезы, но не всегда задумывается о последствиях, связанными с этими экспериментами.
Во время COVID-19, спрос на дорогие товары для охоты сильно упал, компания приняла решение расширить ассортимент и выйти на рынок туризма. Основная причина этого шага - закрытые границы. Отдел маркетинга и анализ рынка предсказывал увеличение внутреннего туризма и спроса на соответствующие товары.
После того, как выбрали новых поставщиков и договорились с ними о сотрудничестве, на мне повисла задача наполнить сайт огромным количеством новых позиций.
Обсудив с коллегами, принял решение спарсить товары с сайтов поставщиков/конкурентов. В этом мне помог АОП, универсальное решение для работы с товарами и ценами в OpenCart.
При поиске донора для парсинга есть одно важное условие - унифицированные характеристики.
Многие конкуренты и поставщики не обращают на это внимание, но для правильной работы фильтров и сравнения товаров, нужно чтобы название характеристики и ее значение было в едином стиле.
Пример:
Увеличение |
1х |
Увеличение, х |
1 |
Это две разные характеристики и два разных значения. Соответственно будут дубли, а это чревато тем, что какое-то количество товара при выборе первого фильтра пользователь не увидит.
У основных поставщиков были проблемы с характеристиками, у конкурентов внутренние артикулы отличаются от прайсов поставщиков. При парсинге товаров, мой выбор пал на конкурентов и это стало огромной ошибкой в последующем.
В моменте никто и не думал о том, каким образом будут обновляться цены.
Но когда количество товара переросло отметку в 15 тысяч, компания столкнулась с:
Нестабильный курс
Санкции и новые пути поставок
РРЦ
Цены менялись ежедневно. Совместно с отделами продаж и контента, провели мозговой штурм, о том, как быстро свести тысячи артикулов из прайсов поставщиков и изменять цены в автоматическом режиме. Назвали нашу идею - ЦУЦ (Центр управления ценами).
ЦУЦ 1.0
Для того, чтобы быстро свести артикулы и обновлять цены нам понадобится:
АОП
KeyCollector
Excel
Делаем выгрузку всех товаров с помощью АОП.
Добавляем название или H1 товара в KeyCollector и собираем данные из нужной поисковой системы. В нашем случае это Яндекс.
Экспортируем данные ПС, получаем таблицу:
Сталкиваюсь с проблемой нерелевантных страниц. Товары отличающиеся каким-то свойством, но имеющие одинаковое описание, характеристики. ПС может отдавать приоритет одной карточке товара. В итоге мы имеем то, что на два разных запроса получаем одну ссылку. Такие случаи приходилось проверять руками.
80% ссылок после анализа ПС ссылалось на верный товар.
Создал таблицу, где отсортировал товары по сайтам конкурентов и поставщиков, добавил внутренние артикулы, распределил товар по брендам и принялся настраивать АОП.
Артикул |
Название товара |
Бренд |
Домен поставщика |
Ссылка конкурента 1 |
Ссылка конкурента 2 |
Запускаем, цены обновились. Ура! Воскликнули все с облегчением. Но, на самом деле, это было только начало.
В данном способе обновления цен были нюансы. Нужно каждый раз проверять файл с ошибками из-за того, что конкуренты не стоят на месте. Многие вносят правки на свои сайты, часто после редактирования ломаются параметры парсинга. Некоторые конкуренты используют js скрипты в своем коде, привязаться к данным становится очень сложно.
Так же, метод обновления через сайты конкурентов/поставщиков, не дал нормально свести артикулы.
В компании пользовались этим способом на протяжении года, пока не решили выйти на рынок маркетплейсов.
ЦУЦ 2.0
Работа с МП подразумевала под собой наличие точной информации по количеству остатков и актуальных цен у поставщиков. Иначе есть высокий риск продать по цене ниже рынка или не доставить заказанный товар из-за его отсутствия.
Причина этого формат работы: МП - Наша компания - Поставщик
Нужно знать точное количество остатков на складе поставщика. Такую информацию поставщики предоставляют только в прайс листах. Но вот незадача, каждый поставщик имеет индивидуальный формат таблицы.
Использовав PHP SimpleXML и SimpleXLSX создаю скрипт, который форматирует прайсы в единый стиль таблицы.
<?php
$curl = curl_init();
$url = 'https://site.com/price.xml';
$fp = fopen("price.xml", "w");
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_FILE, $fp);
curl_setopt($curl, CURLOPT_USERAGENT, "Opera/10.00 (Windows NT 5.1; U; ru) Presto/2.2.0");
$result = curl_exec($curl);
curl_close($curl);
fclose($fp);
error_reporting(E_ALL);
ini_set('display_errors', 'on');
$previous_use_error = libxml_use_internal_errors(true);
//LOADXML
$xml = simplexml_load_file('price.xml');
ob_start();
echo ('<?xml version="1.0" encoding="UTF-8"?>');
echo ('<price>');
foreach ($xml->offers->offer as $offer) {
$offer->name = preg_replace('/</', '<', $offer->name);
$offer->name = preg_replace('/&/', '&', $offer->name);
$offer->name = preg_replace('/ /', ' ', $offer->name);
$offer->stock_msk = preg_replace('/\+\d*/', '', $offer->stock_msk);
$offer->stock_msk = (int)$offer->stock_msk + (int) $offer->stock_msk_shops;
echo '<offer>
<sku>' . $offer->code . '</sku>
<name>' . $offer->name . '</name>
<stock>' . $offer->stock_msk . '</stock>
<price>' . $offer->retail_price . '</price>
</offer>';
}
echo ('</price>');
file_put_contents('price_new.xml', ob_get_contents());
?>
Получаю таблицу формата xml:
Артикул |
Название |
Остаток |
Цена |
0001 |
Товар1 |
0 |
99 |
0002 |
Товар2 |
99 |
199 |
Добавляю товары на сайт прокладку с помощью АОП.
Встроенный функционал АОП позволяет создать CRON для автоматического обновления остатков и цен. Настраиваю CRON для обновления прайсов поставщиков.
Так же настраиваю CRON АОП на основном сайте и сверяю артикулы с прайсами поставщиков.
Конечно прайсы поставщиков не идеальны, существует проблема вывода остатков в формате 0 и 1, в таких случаях все равно приходится запрашивать информацию.
В будущем планируется версия 3.0 - гибрид 1 и 2 версии ЦУЦа.
Я долго шел к настройке ЦУЦ, чтобы автоматизировать магазин, в итоге получил то, что хотел. И самое главное, что отдел продаж остался доволен :) Всем спасибо за внимание!
Комментарии (8)
FanatPHP
00.00.0000 00:00+2Я тоже поставил лайк, и в целом понимаю, что эти придирки не совсем в тему, поскольку статья в основном про другое. Но все же она размещена в хабе РНР, а от единственного фрагмента кода на этом языке просто кровь из глаз.
Опять же, я понимаю, что вины автора нет, все мы ищем решение в интернете, и находим не правильное, а то, которое гуголь счел самым авторитетным сиречь как можно более древним, в идеале — из прошлого века. Но просто на будущее:- Самым примечательным, конечно, является блок preg_replace.
- регулярки для простых строковых замен — это чересчур. Тем более аж две на одну и ту же строку. Не говоря уже о том, что замены HTML сущностей в РНР есть специальные функции, которые делают все сами.
- смысл операции
$offer->name = preg_replace('/ /', ' ', $offer->name);
от меня ускользает :) Наверное, имелось в виду$offer->name = preg_replace('/ +/', ' ', $offer->name);
? -
preg_replace('/\+\d*/', '', $offer->stock_msk)
тоже выглядит довольно странно. Оно действительно делает именно то, что задумано?
- Странное отношение к обработке ошибок. Общие ошибки РНР включаем не почему-то не сразу, а в середине кода. Курл на ошибки вообще не проверяем, как и simplexml, заодно отключив ему возможность сообщать об ошибках самостоятельно. В итоге непонятно, хотим мы видеть ошибки, или нет? По идее, программист, который отключает генерацию ошибок, стреляет себе сразу в обе ноги.
- Курл для простого запроса использовать нет смысла, практически любая функция в РНР умеет работать с НТТР напрямую. Весь этот многострочник заменяется на
copy('https://site.com/price.xml', "price.xml");
. Подделывать юзер агент я не вижу смысла. Даже наоборот это будет выглядеть подозрительно — XML браузерами не скачивают. - В РНР есть такая крутая вещь, как двойные кавычки :) (не говоря уже про heredoc, который здесь подойдет даже лучше, но не будем перегружать)
- Совсем уж мелочь, но использовать буферизацию вывода для собирания строки как-то странно.
Как минимум, я бы переписал так
<?php error_reporting(E_ALL); ini_set('display_errors', 'on'); $url = 'https://site.com/price.xml'; $xml = simplexml_load_file($url); $out = '<?xml version="1.0" encoding="UTF-8"?>'; $out .= "\n<price>"; foreach ($xml->offers->offer as $offer) { $offer->name = htmlspecialchars($offer->name); $offer->stock_msk = preg_replace('/\+\d*/', '0', $offer->stock_msk); $offer->stock_msk = (int)$offer->stock_msk + (int) $offer->stock_msk_shops; $out .= " <offer> <sku>$offer->code</sku> <name>$offer->name</name> <stock>$offer->stock_msk</stock> <price>$offer->retail_price</price> </offer>"; } $out .= "\n</price>\n"; file_put_contents('price_new.xml', $out);
хотя по-хорошему, генерацию XML стоит делать не вручную, а тем же smilexml.
udts Автор
00.00.0000 00:00+2Прошу прощения за кровь из глаз! Безумно благодарен за пояснения и разбор кода. Почерпнул для себя новую информацию! Буду стараться сильнее вникать в PHP.
- Самым примечательным, конечно, является блок preg_replace.
FulgerX2007
00.00.0000 00:00+1???? думаю все кто работает с сайтами по продажам встречали такие задачи.
P.S. вспоминается молодость
kolobo4ek10
00.00.0000 00:00А зачем мучить Яндекс для определения релевантности страницы? Разве нет более оптимального решения?
udts Автор
00.00.0000 00:00Для быстрого сведения товаров с прайсами поставщиков. При первоначальной работе с товарами не было нормальной связки с прайсами поставщиков. Артикулы не совпадали. Можно было сделать связку по названию товара, но они изменялись для поисковой оптимизации. Так что просто ВПР и Эксель тут не подходит :)
TheTrueRikkiTikki
00.00.0000 00:00Не ну круто, а как теперь к этой всей суете прицепить анализ наличия складских остатков?
Phil_itch
Статье лайк, но хорошим тоном считается давать расшифровки всех встречающихся аббревиатур, я про РРЦ: присутствует на КДПВ (Картинка Для Привлечения Внимания =)) ) и в тексте. Не все пользователи Хабра погружены в продажную тему...
udts Автор
Спасибо, учту в следующий раз!