Делаем собственный парсер контента

Делаем собственный парсер контента

24.06.2016

В данной статье будет рассмотрен процесс создания веб-парсинга, который получает общую информацию с сайта.

Технологии, которые использовались:

  • NodeJS: серверный javascript
  • ExpressJS: Фреймворк для приложений Node.js
  • Request: Модуль npm, чтобы скачивать страницы непосредственно в память
  • Cheerio: Позволяет работать со скачанными из сети данными, используя синтаксис, аналогичный jQuery
  • Async: Модуль для асинхронной работы кода
  • Imagemagick: Модуль для работы с изображениями

Использование:

Код ниже — простое приложение, которое получает список элементов выборку и общую информацию о них, такую как:

  • название;
  • раздел;
  • изображение;
  • описание;
  • геолокация;
  • и т.д.

В начале получим список ссылок на детальные страницы городов. Для этого подключаем нужные модули, затем сохраняем в переменную url, ссылку на страницу, которую будем затем «сграбить».

var fs = require('fs'),
request = require('request'),
cheerio = require('cheerio'),
async = require('async');

var url = 'http://example.com/array1/';

После этого используем модуль Request, чтобы скачать страницу, находящуюся по URL, переданному функции request. Передаём в качестве аргументов URL, содержимое которого хотим скачать, и callback, который обработает результаты запроса. Когда данные получены вызываем callback, в который в качестве аргументов будут переданы три переменные:

  • error,
  • response
  • body.

Если Request не сможет скачать страницу и получить данные, он передаст в функцию объект в переменной error и null, в качестве аргумента body. Прежде чем начинать работать с данными, проверяем не произошла ли ошибка; если произошла — выводим сообщение в консоль, чтобы увидеть, что именно пошло не так.

request(url, function (error, response, body) {

if (!error && response.statusCode == 200) {
//продолжаем работу
}
else{
//вывод ошибки и выход из программы
console.log(error);
process.exit();
}
});

Если запрос успешен, то передаем модулю Cheerio полученные данные. В результате можно работать с данными как на обычном сайте, используя стандартный синтаксис jQuery. Чтобы найти на странице интересующие нас данные, надо написать селектор, который получит нужный элемент (или набор элементов). В нашем случаем таким селектором будут являться ссылки типа '.class h2 a', а точнее их атрибут href.

Получив список нужных ссылок, сохраняем их в массиве links.

if (!error && response.statusCode == 200) {

var $ = cheerio.load(body);

var item_link = $('.class h2 a');
var length = item_link.length;

var links = new Array();

for (i = 0; i < length; i++) {
if (typeof item_link[i].attribs.href !== 'undefined') {
links.push(item_link[i].attribs.href);
}
}

}

Если открыть в браузере URL из примера и исследовать страницу с помощью инструментов разработчика, можно увидеть тот самый элемент, для которого написан селектор.

Аналогичным способом получаем информацию с детальных страниц других элементов. Чтобы избежать нагрузки на сервер используем функцию setTimeout, и переменную timeout_elements равную 5 секундам (время задержки, между детальными страницами элементов). Информацию будем сохранять в массив array1. Как и в примере со ссылками, для каждого типа информации пишется свой селектор. Пробежавшись по всем ссылкам массива links и сохранив всю нужную информацию в массиве array1, преобразуем array1 в объект json и сохраняем его в файле array1.json.

var array1 = new array();
var n = 0;
setTimeout(function first_function() {

setTimeout(function second_function(){

request(links[n], function (error, response, body) {

if (!error && response.statusCode == 200) {

var $ = cheerio.load(body),
city = new Object(),
container = $('.content-class');

array1.image = container.find('#image').attr('data-value'),
array1.razdel = container.find('#razdel').text();
array1.description = container.find('#description').text(),
array1.name = container.find('#name').text();
array1.geo = container.find('#geo').attr('href');

array1.push(array1);
}

});

n++;

if(n > links.length){

fs.writeFile("../result/elements/array1.json", JSON.stringify(array1), function(err) {
if(err) {
return console.log(err);
}
console.log("array1Json.json был сохранен!");
console.log('Копирование завершено');

process.exit();
});

}
else{

setTimeout(first_function, timeout_elements);
}

}, timeout_elements);

}, timeout_elements);

На данном этапе парсинг городов закончен. Далее приведем пример работы с изображениями. Функция download_image() будет принимать путь к изображению, после чего сохранит и ресайзит данное изображение:

function download_image(image_path){

var filepath = "../result/" +image_name;
// сохранияем изображение в папке result
request.head(image_path, function(err, res, body){

request(image_path).pipe(fs.createWriteStream(filepath));

});

//обрезаем картинку 100X100 px
im.identify(file, function(err, features){

im.crop({
srcPath: image_path,
dstPath: filepath,
width: 100,
height: 100
}, function(err, stdout, stderr){
return true;
});
});
}

В итоге у нас есть json файл с информацией об элементах и папка с картинками. Сконвертируем json файл в xml, для более удобной выгрузки в 1С-Битрикс. В начале получим элементы из файла array1.json и сохраним их в массиве all_elements. Далее в цикле for пробежимся по all_elements, передавая параметры в функцию write_elements(), которая в свою очередь будет формировать строку str_elements в нужном формате для xml файла Битрикс, и далее сохранив ее в файле array1.xml. Файл для выгрузки готов.

var all_elements = JSON.parse(fs.readFileSync("../result/array1.json", "utf8"));
str_elements = '';

for (i = 0; i < all_elements.length - 1; i++) {
var item = all_elements[i];

var name = item.name;
var geo = item.geo;
var razdel = item.razdel;
var image = item.image;
var preview_text = item.description;

str_elements += write_elements(name, geo, preview_text, razdel, image);

}

fs.writeFile("../result/array1.xml", str_elements, function (err) {
if (err) {
return console.log(err);
}

console.log("array1.xml был сохранен!");
});

function write_elements(name, geo, preview_text, razdel, image) {
return '<Товар>\n' +
' <Ид>' +id+ '\n' +
' <Наименование>' + name + '\n' +
' <БитриксТеги>\n' +
' <Группы>\n' +
' \n' +
' <Картинка>\n' +
' <ЗначенияСвойств>\n' +
' <ЗначенияСвойства>\n' +
' <Ид>CML2_ACTIVE\n' +
' <Значение>true\n' +
' \n' +
...
' <ЗначенияСвойства>\n' +
' <Ид>73\n' +
' <Значение>'+razdel+'\n' +
' <ЗначениеСвойства>\n' +
' <Значение>'+razdel+'\n' +
' <Описание>\n' +
' \n' +
' \n' +
' <ЗначенияСвойства>\n' +
' <Ид>74\n' +
' <Значение>'+geo+'\n' +
' <ЗначениеСвойства>\n' +
' <Значение>'+geo+'\n' +
' <Описание>\n' +
' \n' +
' \n' +
' \n' +
'\n';

}
;