Переменные в парсерах JavaScript

Описание типов переменных в JS парсерах и примеры работы с ними

  1. Support Юра
    Все чаще встречаются сайты, которые требуют индивидуального подхода в процессе парсинга. Здесь, как нельзя лучше подходят решения, созданные с помощью языка программирования JavaScript, который поддерживается в A-Parser.
    Основу любого языка программирования составляют переменные, которые используются для построения логики работы программы. Невозможно начать работать с системой, не зная весь перечень и особенности использования каждой из переменных. В A-Parser существует два типа переменных, которые можно использовать в проектах, созданных с помощью встроенного языка JavaScript. Рассмотрим каждый и попробуем дать исчерпывающую характеристику по их использованию.

    Для наглядности и лучшего понимания материала, напишем простой JS парсер для страницы https://habrahabr.ru/all, который, например, будет собирать название, ссылку на статью и количество новых статей:


    [​IMG]
    Код:
    class Parser {
        constructor() {
            this.defaultConf = {
                version: '0.1.18',
                results: {
                    flat: [
                        ['totalcount', 'Total results count']
                    ],
                    arrays: {
                        serp: ['List', [['link', 'Link'], ['name', 'Name']]]
                    }
                },   
                results_format: "$totalcount\n$serp.format('$link - $name\\n')",
                parsecodes: {
                    200: 1,
                },
                page_count: '1'
            };
    
            this.editableConf = [
                ['page_count', ['combobox', 'PageCount', ['1', '1'], ['2', '2'], ['3', '3'], ['4', '4'], ['5', '5'], ['6', '6'], ['7', '7'], ['8', '8'], ['9', '9'], ['10', '10']]]
            ];
        }
    
        *parse(set, results) {
            let success = 1;
            for(let i = 1; i<=this.conf.page_count; i++){
                let link = set.query + 'page' + i;
                let response = yield this.request('GET', link, {}, {
                    decode: 'utf8',
                });
    
                if(this.conf.page_count == 1) {
                    try {
                        results.totalcount = parseInt(response.data.match(/<span class="tabs-menu__item-counter tabs-menu__item-counter_new">(.+?)<\/span>/)[1]);
                    } catch(e) {
                        this.logger.put('Results count not found');
                    }
                }
    
                if(!response.success) {
                    this.logger.put('Error open page');
                    success = 0;
                    break;
                }
        
                if(results.serp !== undefined) {
                    let regexp = /<h2 class="post__title">\n\s+<a href="(.+?)"\sclass="post__title_link">(.+?)<\/a>/g;
                    let res;
                    while (res = regexp.exec(response.data)) {
                        results.serp.push(res[1], res[2]);
                    }
                }
            }
            results.success = success;
            return results;
        }
    }
    Код:
    eJyNVm1v2zgM/iuaEMDJNXWSbl07p+mhV7SHDcXaW3v3JQ4CxVYSr47kSXJfEPi/
    Hym/JY4POH1oKVKkHpKP6GypYfpZPyiuudHUm25pYmXq0X+YunmjfZowpblC45R+
    e/S8F6Yitoi5BlvIlyyNDZ3N+hT8QNS3Um0Y+neSkVsEq4yP7IU/STAuo5jX6lvY
    fWcbjl4hMxyt7tIG6vZc84YRWBhGJpKCxfkNCKi+9W8R/UrRX0g4C6KKuL5VcgMq
    w20AVL6X6Ka0Y/cUQqTW96/ch3pLFmvepxqg3jIAEjYtkeGKGanuE8QD+i2V4iqO
    7/gLj+tjNv4faRSHUL2rJTh9LRzbj9wfxMiq9HaveuHqVQGG+lAo7+SqzP2Z86Sq
    xnfUbKTiVYDi5iIuND7hIoSTdTOuklq1B3Cv4PvKQIpltLoHaCoKeXkyFU/Arntx
    LTdJzBGxSOMYCq75j7rxV7ooMG5qgE3na3sFwCo516dGylh/e8yhJioCYp1WzQft
    2phEe4PBmi0Uwz+uSgcsjgdwPACarSQQAKBms4rl1UvYNrjubTOo9E/9kJ9DrK2n
    aLVxfyKGIGZak9yLbH1BYEG1tFFpAFTo9kolLrOOtFvkh/mSya4VF1RYQyM94gzd
    kTs6c/r79qI7XtMP1zJmxiPTQwOuqWOkYXEgU2GcPnGecFeGI7l6dug66x/qmFLs
    vR0CLihEAjCcu0jjTdOpE0fiGe+8w/8zUDkChgFqcCg4s1nLxdm+KuvvbFpLMs/n
    iUd82qlT9X3RQUDltHE6CIYckw5C8GEJp+fTRpaWLIEEprdmeTIcemTUbyJsxljx
    ucUAzRw5tTEb+6JBCQ6jDzlVcKLRwqlTx8KCOoHcLORCvmEFH8B0XVlGqBrlNT5B
    +SSXP6L8MZc/ofwpl09RPs3lzyh/zuUzlM9y+Rzl81z+gvKXXB4N7WXD/f7Nxrmc
    lUn+ZovZhVfXL1u19ybg7ROdBgGHZzQho3FtgZZ10RpZPYkuJrZYOIvcuiJgODrq
    bXdrWoa1rZ4AIY1rBx05IraUDgjRuM0DACbweDl4vUc8DvP2KA7uGsjz580T5Ixh
    +2QLlGzhRmh5Ay1PzfK8+Xqz3nhfsb+Llt22DMkE8u81LzOQT0Plm6LAbv0AIBPb
    gK/CdMvsXPgGMxeeQ7DuDi50wgSxY2ziw8+FhT7ecJHO5/AJ2hzbGDDZ/kM/F/zV
    p5dd9+j33oXvDzDW5aA3Hc2amWYksPfxg0RsMph2LFcrrtwkhUr/2B1MREgDbEhF
    6BwU0DQHRbOtUNQPVeIF0f4fhhulpCISvpb2MR/cjatm7rDFulCcPTcLcQCvbBrO
    KfIBmg2Jwk8kwcNWnDlPV/wtgVsHF+uTqnmJ1GY+N5GJOTQFRpvv66MLRtaKL8Fs
    u+RTULY4zJHVu61kl4NVS0rFK2mxvK7hm0kwG8CVA3T5Gw/2addrzQnXbhmgAXqN
    jsAkOzamJweMainmznZHrAJXvSqk8e4ZkypRHq1nWEbxZ0HxVabeKPsXpUhX3g==
    [​IMG]

    Типы переменных
    . При создании JavaScript парсеров в A-Parser доступны два типа переменных: flat и arrays.

    Тип flat используется для определения простых переменных, в которые планируется помещать единичные значения. Соответственно, тип arrays используется для массивов данных. Оба типа могут использоваться как вместе, так и раздельно, в зависимости от исполняемой задачи и получаемых результатов.
    Для более полного понимания того, как устроены переменные в А-Парсере, рекомендуем ознакомиться с этой статьей.

    Работа с типом flat. Для того чтобы начать использовать переменную типа flat, ее нужно объявить в конструкторе:

    Код:
    class Parser {
        constructor() {
            this.defaultConf = {
                version: '0.1.18',
                results: {
                    flat: [
                        ['totalcount', 'Total results count']
                    ],
    В данном примере, мы обьявляем простую (flat) переменную, которая имеет название totalcount. Важно заметить, что обьявление простых переменных состоит из двух частей:
    • имени переменной (totalcount) - значение, которое будет использоваться в самом коде парсера при обращении к переменной
    • описания переменной (Total results count) - используется исключительно для визуального отображения переменной в интерфейсе парсера:
    [​IMG]
    Можно обьявлять сколько угодно простых переменных, например:
    Код:
    flat: [
        ['first', 'Первая переменная'],
        ['second', 'Вторая переменная'],
        ...
        ['n', 'N-я переменная']
    ]
    Работа с типом arrays. Аналогично flat, для использования типа arrays его нужно объявить в конструкторе перед использованием:
    Код:
    class Parser {
        constructor() {
            this.defaultConf = {
                version: '0.1.18',
                results: {
                    flat: [
                        ['totalcount', 'Total results count']
                    ],
                    arrays: {
                        serp: ['List', [['link', 'Link'], ['name', 'Name']]]
                    }
    Структура для объявления arrays отличается от flat, так как используется для вывода массива значений. В структуру включены:
    • имя массива (serp) – для использования в коде
    • заголовок массива (List) - используется для визуального отображения в интерфейсе парсера
    • объявление каждого элемента массива (['link', 'Link']) - имя и название аналогично типу flat
    [​IMG]
    Массивов и элементов, которые в них размещаются, можно объявить столько, сколько требует задание, например:
    Код:
    arrays: {
         arr1: ['Array 1', [['el1', 'First element'], ['el2', 'Second element']]],
         arr2: ['Array 2', [['el', 'Single element']]],
         ...
         arrN: ['Array 3', [['el1', 'First element'], ['el2', 'Second element'], ..., ['elN', 'N-th element']]]
    }
    В нашем примере, мы объявили один массив с названием serp, в котором планируется размещать два элемента link и name, которые будут элементами массива со значениями ссылок и имен соответственно.

    Сохранение в flat. После декларирования (объявления) переменной типа flat, она становится доступной для записи и вывода в результаты. Согласно базовым принципам создания JS парсеров, все результирующие переменные хранятся в объекте results, поэтому все переменные доступны для записи и чтения, через обращение к этому обьекту. В нашем примере мы используем конструкцию:
    Код:
    results.totalcount = parseInt(response.data.match(/<span class="tabs-menu__item-counter tabs-menu__item-counter_new">(.+?)<\/span>/)[1]);
    Т.е. в results.totalcount записывается результат некой функции, которая в данном случае забирает значение из кода страницы с помощью регулярного выражения.

    Таким образом, мы получаем в переменной totalcount значение +39, которое будет доступным для вывода в результаты при использовании в формате результата переменной $totalcount.

    Сохранение в arrays. По аналогии с flat, после декларирования становится доступным использование массивов arrays. Важно понимать, что массивы результатов в JS парсере отличаются от таковых в обычных пресетах. Более детально это показано на примере ниже.

    Объявим в конструкторе такой массив результатов:
    Код:
    arrays: {
         arr1: ['Array 1', [['el1', 'First element'], ['el2', 'Second element']]]
    }
    Согласно общему представлению переменных в А-Парсере, это будет массив, имеющий примерно такую структуру:
    Код:
    {
      "arr1": [
        {
          "el1": "1-1",
          "el2": "1-2"
        },
        {
          "el1": "2-1",
          "el2": "2-2"
        },
        {
          "el1": "3-1",
          "el2": "3-2"
        }
      ]
    }
    Т.е. массив хэшей. Если в формате результата задать такой шаблон:
    Код:
    $arr1.format('$el1, $el2\n')
    то мы получим результат такого вида:
    Код:
    1-1, 1-2
    2-1, 2-2
    3-1, 3-2
    Но, в JS парсерах данный массив имеет такой вид:
    Код:
    {
      "arr1": [
        "1-1",
        "1-2",
        "2-1",
        "2-2",
        "3-1",
        "3-2"
      ]
    }
    Т.е. именно массив, в классическом его виде. А-Парсер автоматически преобразовывает данный "обычный" массив в массив хэшей согласно объявленной в конструкторе структуре.​

    Таким образом, в JS парсерах для записи значений в массив нужно использовать обычные методы для работы с массивами. Чаще всего это .push.
    Реальный пример показан в парсере, о котором упоминалось в начале статьи. В нем регулярным выражением извлекаются из кода страницы необходимые данные и в цикле записываются в массив:
    Код:
    let regexp = /<h2 class="post__title">\n\s+<a href="(.+?)"\sclass="post__title_link">(.+?)<\/a>/g;
    let res;
     while (res = regexp.exec(response.data)) {
         results.serp.push(res[1], res[2]);
    };
    Таким образом, полученные значения будут доступны в парсере, например, при таком формате результата: $serp.format('$link - $name\n'). Значения можно выводить как вместе, так и раздельно, например $serp.format('$link\n'). Более подробно о методе .format в этом разделе Документации.

    Оптимизация. Для оптимизации потребления ресурсов, а также увеличения скорости работы создаваемых парсеров, реализован следующий механизм. Если в формате результата не используется какой-то из объявленных массивов, то он имеет значение undefined. Благодаря этому, есть возможность проверять, нужен ли конечному пользователю результат из данного массива, и если не нужен, то пропускать его парсинг, тем самым экономя время и ресурсы. Например:
    Код:
    if(results.serp !== undefined) {
         let regexp = /<h2 class="post__title">\n\s+<a href="(.+?)"\sclass="post__title_link">(.+?)<\/a>/g;
         let res;
         while (res = regexp.exec(response.data)) {
              results.serp.push(res[1], res[2]);
         }
    }
    Если массив $serp не выводится в результат, то полностью пропускается парсинг всех его элементов, а именно: не применяется регулярное выражение и не запускается цикл для перебора и записи полученных данных.
    При записи результатов в массив, рекомендуется использовать данную проверку. При ее отсутствии, если пользователь не будет выводить массив в результат, то парсер все равно попытается записать результат в массив, и будет получена ошибка, т.к. массив по сути будет не объявлен.

    В этой статье были рассмотрены типы переменных, которые используются при создании JavaScript парсеров. Подробно описан процесс объявления переменных, их заполнение и вывод в результаты. Также продемонстрированы примеры использования каждого типа переменных. Этот материал будет полезен в первую очередь тем, кто планирует начать создавать собственные парсеры на JavaScript.
    ВПР нравится это.