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

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

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

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


var1.png

Код:
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==
7ep7n_180425111354.png

Типы переменных
. При создании 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) - используется исключительно для визуального отображения переменной в интерфейсе парсера:
var2.png

Можно обьявлять сколько угодно простых переменных, например:
Код:
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
var3.png

Массивов и элементов, которые в них размещаются, можно объявить столько, сколько требует задание, например:
Код:
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.
  • Like
Реакции: ВПР
Автор
Support Юра
Просмотры
51
Первый выпуск
Обновление

Рейтинги

5,00 звёзд Оценок: 2

Ещё ресурсы от Support Юра

Назад
Верх