В прошлой статье мы рассмотрели способ разработки парсера, который собирает ТОП 10 в выдаче одной из поисковых систем, а затем по очереди парсит сайты по полученным ссылкам и собирает нужные данные. Вроде все неплохо, но если у вас не 10 запросов, несколько тысяч? Задание будет выполняться очень долго, а время это самый драгоценный и не восполняемый ресурс.
К счастью в A-Parser есть такая замечательная вещь, как многоуровневый парсинг, который позволяет многократно увеличить скорость парсинга, и в этой статье мы рассмотрим как этой возможностью пользоваться.
Общий алгоритм такой:
Все опции как дефолтные так и изменяемые остаются такими же.
- Реализуем метод для сбора содержимого тегов.
- Реализуем метод для сбора ссылок и отправки их на следующий уровень парсинга.
- Опишем в главном методе parse работу этих методов по разным уровням.
Так как парсить мы теперь будем в два уровня, то сбор ссылок с выдачи поисковой системы вынесем для удобства в отдельный метод. Как видно из кода, разница со сбором ссылок из предыдущей статьи, лишь в том что мы не вызываем для каждого витка цикла метод, который собирает содержимое тегов title и description, а отправляем на следующий уровень.Код: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'], ]], ]; }
Для того чтобы запросы не уходили в неудачные если ответ был успешен определяем
и возвращаем массив results.Код:results.success = 1;
Теперь все запросы из следующего уровня будут работать почти одновременно, то есть не в 1 поток перебираются 10 запросов, а 10 запросов сразу. И как результат прирост в скорости очевиден.Код:/*Метод который принимает в качестве параметра запрос по которому будеть парсить один из парсеров поисковых систем*/ * 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; } }
Далее опишем метод который будет работать на следующем уровне. Он будет переходить по ссылке и собирать содержимое тегов title и description.
Теперь давайте в методе parse реализуем парсинг по нескольким уровням. Метод parse является обязательным методом класса Parser, своего рода функция main в языке С.Код:/*Метод записывающий домен сайта и содержимое тегов 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; }
C помощью условного оператора if мы проверяем на каком уровне парсинга мы находимся. Если уровень равен 0 (отсчет уровней начинается с нуля) то вызываем метод parseLinks, который собирает ссылки с выдачи и отправляет их на следующий уровень. Если же уровень парсинга равен единице то вызываем метод parseTitleDescription.Код:* 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; } }
Данный пресет вы можете скачать по этой ссылке.
-
Вступайте в наш Telegram чат: https://t.me/a_parser Нас уже 2600+ и мы растем!Скрыть объявление
Создание JS парсеров. Реализация подстановки запросов и их многопоточной обработки.
Парсим топ10 и содержимое тегов title и description в многопоточном режиме
Метки: