1. Вступайте в наш Telegram чат: https://t.me/a_parser Нас уже 1500+ и мы растем!
    Скрыть объявление

Сбор характеристик товара

Сбор неизвестного количества характеристик товара

  1. Support Alexander
    В этом уроке будет рассмотрен пример парсинга неизвестного количества характеристик товара.
    Готовые пресеты можно посмотреть здесь

    [​IMG]

    В качестве примера мы взяли сайт arcpalace.com.ua
    Для того чтобы наш парсер не занял всю память, мы используем встроенную БД Sqlite для временного хранения данных.
    Из-за некоторых ограничений А-Парсер'a пришлось разбить задание на 2 пресета которые объединены в цепочку.
    Весь принцип работы данного пресета описан ниже.
    Пресет 1:
    1. Парсим ссылки на товары.
    Пресет 2:
    1. Используя файл результата из пресета 1 парсим информацию о товарах.
    2. Пишем всю информацию в БД.
    3. В конце задания выгружаем данные в .csv файл.

    Пресет 1
    [​IMG]
    Думаю тут все понятно, в массив links заносим ссылки на товары и также реализована пагинация для перехода между страницами.
    Для передачи файла результатов в пресет 2, используем опцию Запустить по завершению.
    [​IMG]

    Пресет 2
    [​IMG]
    titile - название товара, features - характеристики, свойства (name - название характеристики, value значение), price - цена, code - артикул.

    Для лучшего понимания рассмотрим сайт донор.
    [​IMG]

    Теперь разберем код в поле Формат результата.
    Код:
    [% CALL tools.sqlite.run(db, "CREATE TABLE IF NOT EXISTS products(code TEXT PRIMARY KEY, title TEXT, price INTEGER)");
    CALL tools.sqlite.run(db, "CREATE TABLE IF NOT EXISTS columns(name TEXT PRIMARY KEY)");
    
    row = tools.sqlite.get(db, "SELECT code FROM products WHERE code = ?", p1.code);
    IF !row.0.code;
        CALL tools.sqlite.run(db, "INSERT INTO products(code, title, price) VALUES(?, ?, ?)", p1.code, p1.title, p1.price);
        FOREACH item IN p1.features;
            column = tools.sqlite.get(db, "SELECT COUNT(*) AS exist FROM pragma_table_info('products') WHERE name = ?", item.name);
            IF !column.0.exist;
                CALL tools.sqlite.run(db, "ALTER TABLE products add `" _ item.name _ "` TEXT");
                CALL tools.sqlite.run(db, "INSERT INTO columns(name) VALUES(?)", item.name);
            END;
            CALL tools.sqlite.run(db, "UPDATE products SET `" _ item.name _ "` = ? WHERE code = ?", item.value, p1.code);
        END;
    END %]
    Для работы с БД используем встроенные функции А-Парсер'a tools.sqlite.*
    Код:
    CALL tools.sqlite.run(db, "CREATE TABLE IF NOT EXISTS products(code TEXT PRIMARY KEY, title TEXT, price INTEGER)");
    CALL tools.sqlite.run(db, "CREATE TABLE IF NOT EXISTS columns(name TEXT PRIMARY KEY)");
    
    Создаем две таблицы products - информация о товарах, columns - уникальные название характеристик.
    СALL - позволяет выполнить запрос, но не печатает возвращаемый результат.

    Код:
    row = tools.sqlite.get(db, "SELECT code FROM products WHERE code = ?", p1.code);
    Проверяем есть ли такой товар в базе.

    Код:
    column = tools.sqlite.get(db, "SELECT COUNT(*) AS exist FROM pragma_table_info('products') WHERE name = ?", item.name);
    Данная выборка позволяет проверить существует ли указанная колонка в таблице, в результате запроса будет 1 или 0.
    pragma_table_info('products') - выводит все колонки из таблицы products.

    Рассмотрим код в Конечном тексте, который выполняется в конце задания.
    Код:
    [%
    cols = [];
    FOREACH col IN tools.sqlite.all(db, "SELECT * FROM columns");
        cols.push(col.name);
    END;
    cols = cols.sort;
    cols.unshift('title', 'price');
    cols.join(';') _ "\n";
    
    USE Math;
    rows = tools.sqlite.get(db, "SELECT COUNT(*) AS count FROM products");
    blockSize = 100;
    blocksCount = Math.int(rows.count / blockSize + 0.999);
    
    FOREACH b IN [1..blocksCount];
        block = tools.sqlite.all(db, "SELECT * FROM products WHERE rowid > ? AND rowid <= ?", (b - 1) * blockSize, b * blockSize);
        FOREACH row IN block;
            FOREACH col IN cols;
                row.${col} _ ';';
            END;
            "\n";
        END;
    END %]
    Код:
    cols = cols.sort;
    cols.unshift('title', 'price');
    cols.join(';') _ "\n";
    cols.sort - метод sort позволяет отсортировать элементы в массиве
    Код:
    list = [ 'c', 'b', 'a' ]
    list.sort = >  [ 'a', 'b', 'c' ] 
    unshift() - добавляет элементы в начало массива, а push() - в конец массива.
    join() - объединяет все элементы массива в строку, в качестве параметра указывается разделитель в нашем случае это точка с запятой.

    Чтобы не выгружать весь результат сразу, так как это может съесть всю память, мы будем подгружать данные блоками.
    Код:
    rows = tools.sqlite.get(db, "SELECT COUNT(*) AS count FROM products");
    blockSize = 100;
    blocksCount = Math.int(rows.count / blockSize + 0.999);
    В rows записываем общее количество товаров, blockSize - размер блока, blocksCount - количество блоков.
    Например в rows получилось 1501 строк, Math.int(1501/100) = 15.01 => 15 так как целое число и меньше 0.5 тогда оно округляется в меньшую сторону, 15 * 100 = 1500 а строк 1501 получается что 1 строка пропадает нужно прибавить 0.999, Math.int(1501/ 100 + 0.999) = 16 * 100 = 1600 теперь все строки попадаю в заданный диапазон 1 - 1600;

    Код:
    FOREACH b IN [1..16];
        block = tools.sqlite.all(db, "SELECT * FROM products WHERE rowid > ? AND rowid <= ?", (1 - 1) * 100, 1 * 100);
    
    После подстановки значений вместо переменных, можно увидеть процесс работы.