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