Создание 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 Денис
Просмотры
9
Первый выпуск
Обновление

Рейтинги

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

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

Назад
Верх