Создание JS парсеров. Реализация подстановки запросов и их многопоточной обработки.

Создание JS парсеров. Реализация подстановки запросов и их многопоточной обработки.

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

Общий алгоритм такой:
  1. Реализуем метод для сбора содержимого тегов.
  2. Реализуем метод для сбора ссылок и отправки их на следующий уровень парсинга.
  3. Опишем в главном методе parse работу этих методов по разным уровням.
Все опции как дефолтные так и изменяемые остаются такими же.
Код:
class Parser {
    constructor() {
        this.defaultConf = {
            results: {
                arrays: {
                    /*Хеш serp в котором будут переменные
                     domain, title и description*/
                    serp: ['Top 10 domains and title', [
                        ['domain', 'domain name'],
                        ['title', 'the contents of the title tag'],
                        ['description', 'the contents ot the description tag'],
                    ]],
                }
            },
            /*Формат результата*/
            results_format: "$serp.format('$domain - $title - $description\\n')",
            /*Код ответа*/
            parsecodes: {
                200: 1,
            },
            /*Максимальный размер ответа*/
            max_size: 200 * 1024,
            search_engine: 'SE::Google::Modern', //Парсер поисковика по умолчанию.
            SE_Google_Modern_preset: 'default', //Пресеты для парсеров по умолчанию
            SE_Yandex_preset: 'default',
            SE_Bing_preset: 'default',
        };
        this.editableConf = [
            ['SE_Google_Modern_preset', ['combobox', 'SE::Google::Modern preset']],
            ['SE_Yandex_preset', ['combobox', 'SE::Yandex preset']],
            ['SE_Bing_preset', ['combobox', 'SE::Bing preset']],
            ['search_engine', ['combobox', 'Search engine', ['SE::Google::Modern', 'Google'],
                ['SE::Yandex', 'Yandex'],
                ['SE::Bing', 'Bing'],
            ]],
        ];
    }
Так как парсить мы теперь будем в два уровня, то сбор ссылок с выдачи поисковой системы вынесем для удобства в отдельный метод. Как видно из кода, разница со сбором ссылок из предыдущей статьи, лишь в том что мы не вызываем для каждого витка цикла метод, который собирает содержимое тегов title и description, а отправляем на следующий уровень.
Для того чтобы запросы не уходили в неудачные если ответ был успешен определяем
Код:
   results.success = 1;
и возвращаем массив results.
Код:
/*Метод который принимает в качестве параметра запрос по которому будеть парсить
     один из парсеров поисковых систем*/
    * parseLinks(key,results) {
        let response = yield this.parser.request(
            this.conf.search_engine, //Парсить будем выбранным парсером.
            this.conf[this.conf.search_engine.replace(/::/g, '_') + '_preset'], { //Указываем пресет
                pagecount: 1, //задаем через override одну страницу
                linksperpage: 10, //задаем через override 10 результатов на страницу
            }, key);

        if (response.info.success) {
            let step;
            /*Устанавливаем шаг прохода по циклу в зависимости от парсера.
            Для SE::Google::Modern и SE::Bing 3, а для SE::Yandex 6*/
            if (this.conf.search_engine == 'SE::Google::Modern' || this.conf.search_engine == 'SE::Bing') {
                step = 3;
            } else {
                step = 6;
            }
            /*Отправляем запросы на уровень парсинга 1*/
            for (let i = 0; i < response.serp.length; i += step) {
                  this.query.add(response.serp[i],1)
            }
            /*Пищем в результат что запрос успешный*/
            results.success = 1;
            return results;
        }
          else {
            this.logger.put(`Error scraping ${this.conf.search_engine}`);
            /*Пищем в результат что запрос неудачный*/
            results.success = 0;
            return results;
        }
    }
Теперь все запросы из следующего уровня будут работать почти одновременно, то есть не в 1 поток перебираются 10 запросов, а 10 запросов сразу. И как результат прирост в скорости очевиден.

Далее опишем метод который будет работать на следующем уровне. Он будет переходить по ссылке и собирать содержимое тегов title и description.
Код:
 /*Метод записывающий домен сайта и содержимое тегов title и descritpion
       в массив results и возвращающий его*/
    * parseTitleDescription(set,results) {
        let response = yield this.request('GET', set.query, {}, {
            decode: 'auto-html',
        });
        if (response.success) {
            let title = response.data.match(/<title>([\s\S]+?)<\/title>/);
            let description = response.data.match(/meta.+?=".*?description"\s+content="(.*?)"/);
            if(!title){
                title = [];
                title[1] = 'none';
                }
            if(!description){
                description = [];
                description[1] = 'none';
            }
             /*Записываем в массив результатов. С помощью this.utils.url.extractDomain достаем из ссылки только домен*/
                results.serp.push(this.utils.url.extractDomain(set.query),title[1],description[1]);
              results.success = 1;
        }
        return results;
    }
Теперь давайте в методе parse реализуем парсинг по нескольким уровням. Метод parse является обязательным методом класса Parser, своего рода функция main в языке С.
Код:
 * parse(set, results) {
        /*Если уровень парсинга нулевой*/
        if (set.lvl == 0) {
            /*Записываем в переменную key запрос*/
            results = yield* this.parseLinks(set.query,results);
            return results;
       }
       /*Если уровень парсинга равен 1*/
       if (set.lvl == 1){
           results = yield* this.parseTitleDescription(set,results);
           return results;
       }
    }
C помощью условного оператора if мы проверяем на каком уровне парсинга мы находимся. Если уровень равен 0 (отсчет уровней начинается с нуля) то вызываем метод parseLinks, который собирает ссылки с выдачи и отправляет их на следующий уровень. Если же уровень парсинга равен единице то вызываем метод parseTitleDescription.

Данный пресет вы можете скачать по этой ссылке.
Автор
Support Денис
Просмотры
42
Первый выпуск
Обновление

Рейтинги

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

Ещё ресурсы от Support Денис

Назад
Верх