Проверка фото из отзывов на AliExpress

На AliExpress пользователи очень часто оставляют отзывы о товарах. Обычно это рассказы о качестве товаров, личные впечатления об использовании и комментарии о сервисах и сроках доставки.

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

По фотографии можно сразу увидеть качество товара в реальности. Которое в большинстве случаев очень сильно отличается от того что рекламируется.

Но есть одно «Но». Фото в отзывах ужасно маленького размера и нет галереи, где можно прокликать все фото за один раз.
Пример того, как выглядят фото в отзывах на AliExpress — bit.ly/2C7myX3

Чтобы исправить ситуацию я решил написать свой парсер фото из товаров и сделать плиточный дизайн в стиле Pinterest.

Адрес сайта — alipic.net.

На момент написания статьи доступны следующие фичи:
1. Парсер фото из отзывов по URL товара.
2. Поиск товаров с максимальным рейтингом по ключевому слову.
3. Вывод Топ продаваемых товаров на AliExpress за неделю.

Чтобы понять зачем это все нужно, давайте посмотрим на выдачу комментариев на AliExpress и Alipic — bit.ly/2o1e1Tg

Товар на AliExpress — bit.ly/2nI7v3T
Фото отзывов на Alipic — bit.ly/2j7zoRe

Далее расскажу о технической реализации парсера фото на php.

Страница продукта состоит из двух блоков — bit.ly/2yqVujg
1. Информация о товаре (цена, название, рейтинг, скидка, ссылка на магазин). Реализовано через API AliExpress.

2. Фото отзывов. Реализовано с помощью прямого парсинга с помощью CURL + SimpleDom.

Для получения API нужно зарегистрировать на AliExpress Developer. Проверка акаунта заняла 5 дней.

Парсим фото из отзывов по URL товара.

В API AliExpress нет доступа к фото из отзывов. Потому смотрим откуда Али вытягивает комментарии:

Открываем страницу любого продукта (в примере — bit.ly/2j7zoRe), запускаем девелоперскую консоль и ищем блок с комментарием — bit.ly/2jSjrLW

Как видим, Али подгружает комментарии в <iframe>. Это очень хорошо, так как не нужно будет грузить полностью всю страницу.

Прямой URL коментариев с параметрами: feedback.aliexpress.com/...​startValidDate=&i18n=true

Параметры которые важны:
productId — уникальный айдишник каждого товара.
withPictures — включить, выключить комментарии с фото. Значения true,false.

Урлы товара бывают разного формата. Для получения productId из урла используем regexp:

$primary_url = filter_input(INPUT_GET, "url");

if (strpos($primary_url, '//group.') == 0) {
    preg_match_all("/(\w+\.\w{2,4})/", $primary_url, $prod_id);
    $prod_id[0][2] = str_replace('.html', '', $prod_id[0][2]);
    $prod_id[0][2];
    $prod_id_url = $prod_id[0][2];
}

$prod_id_url_num = $prod_id_url;

if (strpos($primary_url, '//group.') != 0) {
    $prod_id_url_num_1 = explode('-', $primary_url);
    $prod_id_url_num = $prod_id_url_num_1[1];
}

if (strpos($primary_url, '/store/') != 0) {
    $prod_id_url_num_1 = explode('_', $prod_id_url_num);
    $prod_id_url_num = $prod_id_url_num_1[1];
}

После получения productId готовим ссылки для парсинга:

$prod_id_url = "feedback.aliexpress.com/display/productEvaluation.htm?productId=" . $prod_id_url_num . "&ownerMemberId=221794469&companyId=231687423&memberType=seller&startValidDate=&i18n=true&withPictures=true&translate=N&evaStarFilterValue=all+Stars";

$prod_url_api = "http://gw.api.alibaba.com/openapi/param2/2/portals.open/api.getPromotionProductDetail/".$ali_api_key."?fields=originalPrice,salePrice,discount,evaluateScore,volume,storeUrl,productTitle,imageUrl,productUrl,storeName&language=" . $lang . "&productId=" . $prod_id_url_num . "";

Далее нам нужно параллельно загружать API запрос и парсить страницу с комментариями.
Функция параллельного парсинга:

function multi_parser($url) {
    $ch = array();
    $responce = array();
    $mh = curl_multi_init();
    for ($i = 0; $i < sizeof($url); $i++) {
        $ch[$i] = curl_init($url[$i]);
        curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch[$i], CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch[$i], CURLOPT_HEADER, 0);

        $agent = "AAPP Application/1.0 (Windows; U; Windows NT 5.1; de; rv:1.8.0.4)";

        curl_setopt($ch[$i], CURLOPT_URL, $url[$i]);
        curl_setopt($ch[$i], CURLOPT_TIMEOUT, 10);
        curl_setopt($ch[$i], CURLOPT_MAXREDIRS, 50);
        curl_setopt($ch[$i], CURLOPT_COOKIEJAR, 'cookie.txt');
        curl_setopt($ch[$i], CURLOPT_COOKIEFILE, 'cookie.txt');
        curl_multi_add_handle($mh, $ch[$i]);
    }

    $running = null;
    do {
        curl_multi_exec($mh, $running);
    } while ($running);

    //close all and get content as string
    for ($i = 0; $i < sizeof($url); $i++) {
        curl_multi_remove_handle($mh, $ch[$i]);
        $responce[$i] = curl_multi_getcontent($ch[$i]);
    }

    curl_multi_close($mh);
    return array($responce);
}

Получение контента страниц в массив:

$url_compose [0] = $prod_id_url;
$url_compose [1] = $prod_url_api;
$multi_urls = array_values($url_compose);
list($urls) = multi_parser($multi_urls);

Теперь парсим информацию о товаре, которую получили в json формате:

foreach ($json as $key) {
    $price_discount = $key->originalPrice;
    $price = $key->salePrice;
    $discount_rate = $key->discount;
    $rate = $key->evaluateScore;
    $orders = $key->volume;
    $store_title = $key->storeName;
    $store_link = $key->storeUrl;
    $prod_title = $key->productTitle;
    $thumb_src = $key->imageUrl;
    break;
}

originalPrice - Цена товара без скидки.
salePrice - Цена товара с учетом скидки.
discount - Скидка.
evaluateScore - Рейтинг магазина.
volume - Количество товаров доступных для продажи.
storeName - Название магазина продавца.

Для парсинга страницы комментариев используем SimpleDom библиотеку и обращаемся к DOM элементам страницы:

$html = str_get_html($urls[0], false, stream_context_create($arrContextOptions));

if ($html) {
    foreach ($html->find('input [id="cb-withPictures-filter"]  em') as $element) {

        $pages = $element->innertext;
        $pages = ceil($pages / 10);
        $images = $element->innertext;
    }
    if (!$images) {
        $images = 0;
    }


    foreach ($html->find('p[class="r-score-des"]') as $element) {
        $rating = $element->innertext;
    }
    $urls_list = array();
    $pages_left = $pages;


    if ($cur_page == '') {
        $cur_page = 1;
    }

    for ($i = $cur_page; $i <= $cur_page; $i++) {
        $urls_list[$i] = $prod_id_url . "&page={$i}";
    }

    $multi_urls = array_values($urls_list);
    list($urls) = multi_parser($multi_urls);

    $all_ali_img = array();
    $n = 0;
    for ($i = 0; $i < sizeof($urls); $i++) {
        $arrContextOptions = array(
            "ssl" => array(
                "verify_peer" => false,
                "verify_peer_name" => false,
            ),
        );
        $html = str_get_html($urls[$i], false, stream_context_create($arrContextOptions));

        foreach ($html->find('img') as $element) {

            $all_ali_img[$n] = $element->src;
            $n++;
        }

        //comments counter
        $com_num = 0;
        $glob_array = array();
        $img_array = array();
        $stars_array = array();
        $comment_array = array();
        $devilery_array = array();
        $date_array = array();
        $flag_array = array();

        foreach ($html->find('div[class=feedback-item]') as $element) {
            $img_ar = 0;
            $star_ar = 0;
            $com_ar = 0;
            $delivery_ar = 0;
            $date_ar = 0;
            $flag_ar = 0;

            //img urls
            foreach ($element->find('ul[class=util-clearfix] li img') as $img) {
                $img_array[$img_ar] = $img->src;
                $img_ar++;
            }
            $glob_array[$com_num][1] = $img_array;

            //stars
            foreach ($element->find('span[class=star-view] span') as $stars) {
                $stars_array[$star_ar] = $stars->style;
                $stars_array[$star_ar] = str_replace('width:', '', $stars_array[$star_ar]);
                $star_ar++;
            }
            $glob_array[$com_num][2] = $stars_array;

            //comment
            foreach ($element->find('dt[class=buyer-feedback] span') as $comment) {
                $comment_array[$com_ar] = $comment->innertext;
                $com_ar++;
            }
            $glob_array[$com_num][3] = $comment_array;

            //delivery
            foreach ($element->find('div[class=user-order-info] span') as $delivery) {

                $devilery_array[$delivery_ar] = $delivery->innertext;
                $devilery_array[$delivery_ar] = str_replace('Logistics:', '', $devilery_array[$delivery_ar]);
                $devilery_array[$delivery_ar] = str_replace('Size:', '', $devilery_array[$delivery_ar]);
                $devilery_array[$delivery_ar] = str_replace('Color:', '', $devilery_array[$delivery_ar]);

                if ($lang == 'en') {
                    $devilery_array[$delivery_ar] = str_replace('Доставка', 'Logistics', $devilery_array[$delivery_ar]);
                }

                $delivery_ar++;
            }
            $glob_array[$com_num][4] = $devilery_array;

            //date
            foreach ($element->find('dd[class=r-time]') as $date) {
                $date_array[$date_ar] = $date->innertext;
                $date_ar++;
            }
            $glob_array[$com_num][5] = $date_array;

            //flag
            foreach ($element->find('div[class=user-country] b[class=css_flag]') as $flag) {
                $flag_array[$flag_ar] = $flag->innertext;
                $flag_ar++;
            }
            $glob_array[$com_num][6] = $flag_array;
            $com_num++;
        }
    }
    $url_valid = 1;
} else {
    $url_valid = 0;
}

Для отрисовки фото в стиле Pinterest использовалась библиотека Mansory.JS
Исходный код парсера на github — github.com/...​ter/ali_exp_img_parse.php

👍НравитсяПонравилось0
В избранноеВ избранном0
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Привет. Все супер, но так и не нашел где он этот AliExpress Developer что бы API попросить)))

Но есть одно «Но». Фото в отзывах ужасно маленького размера и нет галереи, где можно прокликать все фото за один раз.

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

Девушки охотно демонстрируют фото себя в купленном нижнем белье

Совсем неприкрытый маркетинговый ход со знанием специфики аудитории :)

Ну классика жанра к такой хрени прикручивать экстеншен браузера. Ибо не станет юзер ходить на какой-то сайт и вбивать ссылки, у него лапки.

PS. Ещё бы нейросеть, которая по тексту и картинке догадывалась что оно такое на самом деле.

Код набагато краще виглядав би на гітхабі а тут тільки ссилки, бо так доволі незручно читати.

Moжно по кейворду искать. Например „Sexy bra” — alipic.net/...​img&url=sexy bra&key=true

Прикольно конечно. Этакий программерский подход:) Гуманитарий нажмёт правую кнопку мышки и выберет «показать изображение»

Молодец. Но:
— где свойства?
— где шаблоны?
— где partial-классы?
— где расширения методов класса?
— где скрытая имплементация интерфейсов?
— где перегрузка параметров функций?
— где нормальное потребление памяти приложением?
— где быстрая работа приложения?
— где нормальные иде, с полноценными дизайнерами?
— где пользовательские value types?
— где методы у инстансов value types?
— где var и анонимные типы
— где перегрузка операторов?

А зачем это все вменяемым и свободным разработчикам собственных продуктов? Фреймворки придуманы, чтобы из заказчиков деньги доить. Как и OOP с OOD. IT давным-давно уже пытается усложнить и переусложнить себя самое. Но главное ведь не лизнуть авторитетам, а написать код, решающий поставленную задачу. Не код, который будет решать целый комплекс возможных осложнений при решении возможных вариаций поставленной задачи так как такой г*код нужен отнюдь не разработчику, а заказчику, оптимизирующему рабочее время разработчика в свою пользу. Все, что не относится к решению текущей задачи выбрасывайте из кода нахрен ибо эти штуки вредны и опасны — стремительно приближают дедлайны. Завтра будет новый день и новый код — говорите себе. И даже, в идеале, лучше копипастить, чем рефакторить. Не бывает двух одинаковых бонсай. Даже в самом начале это два разных зерна или веточки. Так что или вы разбираетесь в программировании или вы используете дизайн паттернс.

Ну не совсем. Например в коде выше вместо голого курла можно газзл заюзать — уже читать приятнее бы было.

и ни одного паттерна проектирования

— где костыли?
— где руль и педали?
— собаки пятая нога — где?

На PHP можно писать довольно быстрый код. Конечно, не такой резкий, как заточенный под конкретную задачу инстанс nodejs. Можно банально придерживаться паттерна mvc и не погрязнуть в паутине спагетти-скриптов с сотнями инклюдов. Код будет хотя бы структурирован и изолирован от мусора. Это идеальное состояние, если большую часть рабочего времени вы добавляете в общую свалку новые, независимые друг от друга конвертики с тухлятиной.
Можно написать классы объектов, там где они необходимы и наполнить их методами. Опять же, всё ради структурирования кода, для вполне сносной и быстрой навигации по разрастающейся выгребной яме проекта.
Можно навесить плюшки в виде аякса, где это уместно, и местами перенести генерацию контента вовсе на клиент, вместе с тем сэкономив десятки тяжёлых запросов на отрисовку страницы целиком.
Можно придти к мысли, что mysql с её слоупочными table locks и transactional safety и с её возможностью масштабирования только при помощи анальных расширителей не очень-то, собственно, и нужна в большинстве задач.
Только это всё не нужно. Ваш сайт, в общем, далеко все не идеально. Но и не все так мрачно, как описано: сайт развивается, у него есть пусть небольшое, но стабильное коммьюнити и, главное, есть пользователи, которые будут зависеть от этого сайта, поэтому сильно заинтересованы в его улучшении, а с недавних пор могущие в этом улучшении участвовать.

Отпишитесь через 3 месяца и расскажите о результатах, как продвигается ваш стартап. Интересно, насколько востребовано.

Интересная реализация псевдомногопоточности в cURL...Но конечно же, я бы выбрал Python для такого проекта....

Ухты, для покупки шмоток полезная штука.

Подписаться на комментарии