Парсеры на JavaScript: Обзор возможностей
JavaScript парсеры - это возможность создавать собственные полноценные парсеры со сколько угодно сложной логикой, используя язык JavaScript. При этом в JS парсерах можно также использовать весь функционал стандартных парсеров.
Особенности
Используя всю мощь A-Parser теперь можно написать свой парсер/регер/постер со сколько угодно сложной логикой. Для написания кода используется JavaScript с возможностями ES6 (движок v8).
Код парсеров максимально лаконичен, позволяет сосредоточится на написании логики; работу с многопоточностью, сетью, прокси, результатами, логами и т.д. A-Parser берет на себя. Код можно писать прямо в интерфейсе парсера, добавив новый парсер в Редакторе парсеров. Также для написания парсеров можно использовать сторонние редакторы, например VSCode.
Используется автоматическое версионирование при сохранении кода парсера через встроенный редактор.
Работа с JavaScript парсерами доступна для лицензий Pro и Enterprise
Доступ к Редактору JS парсеров
Если А-Парсер используется удаленно, то в целях безопасности Редактор JS парсеров по умолчанию не доступен. Чтобы открыть к нему доступ, необходимо:
- Установить пароль на вкладке Настройки -> Общие настройки
- Добавить в config/config.txt следующую строчку:
allow_javascript_editor: 1
- Перезапустить A-Parser
Инструкция по работе
В Редакторе парсеров создаем новый парсер и задаем имя парсера. По умолчанию будет загружен простой пример, на основе которого можно быстро приступить к созданию собственного парсера.
Если для написания кода используется сторонний редактор, то нужно открыть файл редактируемого парсера в папке /parsers/
. Структура файлов установленной программы.
Когда код будет готов, сохраняем его и используем как обычный парсер: в Редакторе заданий выбираем созданный парсер, при необходимости можно задать нужные параметры, конфиг потоков, имя файла и т.п.
Созданный парсер можно в любой момент редактировать. Все изменения, касающиеся интерфейса, появятся после повторного выбора парсера в списке парсеров или перезапуска А-Парсера; изменения в логике парсера применяются при повторном запуске задания с парсером.
Для каждого созданного парсера по умолчанию отображается стандартная иконка, можно добавить свою в формате png или ico, разместив ее в папке парсера в /parsers/
:
Общие принципы работы
По умолчанию создается пример простого парсера, готовый для дальнейшего редактирования.
- TypeScript
- JavaScript
import { BaseParser } from 'a-parser-types';
export class JS_v2_example extends BaseParser {
static defaultConf: typeof BaseParser.defaultConf = {
version: '0.0.1',
results: {
flat: [
['title', 'HTML title'],
]
},
max_size: 2 * 1024 * 1024,
parsecodes: {
200: 1,
},
results_format: '$query: $title\\n',
};
static editableConf: typeof BaseParser.editableConf = [];
async parse(set, results) {
this.logger.put("Start scraping query: " + set.query);
let response = await this.request('GET', set.query, {}, {
check_content: ['<\/html>'],
decode: 'auto-html',
});
if (response.success) {
let matches = response.data.match(/<title>(.*?)<\/title>/i);
if (matches)
results.title = matches[1];
}
results.success = response.success;
return results;
}
}
const { BaseParser } = require("a-parser-types");
class JS_v2_example_js extends BaseParser {
static defaultConf = {
version: '0.0.1',
results: {
flat: [
['title', 'HTML title'],
]
},
max_size: 2 * 1024 * 1024,
parsecodes: {
200: 1,
},
results_format: '$query: $title\\n',
};
static editableConf = [];
async parse(set, results) {
this.logger.put("Start scraping query: " + set.query);
let response = await this.request('GET', set.query, {}, {
check_content: ['<\/html>'],
decode: 'auto-html',
});
if (response.success) {
let matches = response.data.match(/<title>(.*?)<\/title>/i);
if (matches)
results.title = matches[1];
}
results.success = response.success;
return results;
}
}
Конструктор вызывается однократно для каждого задания. Необходимо обязательно задать this.defaultConf.results
и this.defaultConf.results_format
, остальные поля необязательны и будут принимать значения по-умолчанию.
Массив this.editableConf
определяет какие настройки могут быть изменены пользователем из интерфейса A-Parser. Можно использовать следующие типы полей:
combobox
- выпадающее меню выбора. Также можно сделать меню выбора пресета стандартного парсера, например:
['Util_AntiGate_preset', ['combobox', 'AntiGate preset']]
combobox
с возможностью множественного выбора. Нужно дополнительно задать параметр{'multiSelect': 1}
:
['proxyCheckers', ['combobox', 'Proxy Checkers', {'multiSelect': 1}, ['*', 'All']]]
checkbox
- чекбокс, для параметров которые могут иметь только 2 значения (true/false)textfield
- текстовое полеtextarea
- текстовое поле с многострочным вводом
Метод parse
представляет собой асинхронную функцию, и на любую блокирующую операцию должна возвращать await (это основное и единственное отличие от обычной функции). Метод вызывается для каждого запроса поступившего в обработку. Обязательно передается set
(хэш с запросом и его параметрами) и results
(пустая заготовка для результатов). Также обязательно необходимо вернуть заполненый results
, предварительно выставив флаг success.
Автоматическое версионирование
Версия имеет формат Major.Minor.Revision
- TypeScript
- JavaScript
this.defaultConf: typeof BaseParser.defaultConf = {
version: '0.1.1',
...
}
this.defaultConf = {
version: '0.1.1',
...
}
Значение Revision (последняя цифра) автоматически увеличивается при каждом сохранении. Остальные значения (Major, Minor) можно менять вручную, а также сбрасывать Revision в 0
.
Если по каким то причинам необходимо менять Revision только вручную, то версию необходимо заключить в двойные кавычки ""
Пакетная обработка запросов
В некоторых случаях может потребоваться брать сразу несколько запросов из очереди и обрабатывать их за один раз. Например, такой функционал используется в SE::Yandex::Direct::Frequency: при парсинге с аккаунтами данные собираются пачками по 10 запросов.
Чтобы реализовать такой же функционал в JS парсере, нужно в this.defaultConf
задать значение bulkQueries: N
, где N - необходимое количество запросов в пачке. В этом случае парсер будет брать запросы пачками по N штук и все запросы текущей итерации будут содержатся в массиве set.bulkQueries
(включая все стандартные переменные: query.first
, query.orig
, query.prev
и т.п.). Ниже пример такого массива:
[
{
"first": "test",
"prev": "",
"lvl": 0,
"num": 0,
"query": "test",
"queryUid": "6eb301",
"orig": "test"
},
{
"first": "проверка",
"prev": "",
"lvl": 0,
"num": 1,
"query": "проверка",
"queryUid": "774563",
"orig": "проверка"
},
{
"first": "third query",
"prev": "",
"lvl": 0,
"num": 2,
"query": "third query",
"queryUid": "2bc8ed",
"orig": "third query"
}
]
Результаты при пакетной обработке необходимо заполнять в массиве results.bulkResults
, где каждый элемент является обьектом results
. Элементы в results.bulkResults
расположены в той же очередности, которая была в set.bulkQueries
.
Полезные ссылки
📄️ Пример bulkQueries
Пример использования bulkQueries с вызовом встроенного парсера
🔗 Примеры и обсуждение
Тема на форуме с примерами и обсуждением функционала JS парсеров
🔗 Каталог JS парсеров
Раздел в каталоге ресурсов, посвященный JS парсерам
🔗 Обзор базовых возможностей ES6
Статья на habrahabr посвященная обзору базовых возможностей ES6