Другим преимуществом, ускоряющим процесс разработки, является поддержка npm (Node.js package manager) - менеджер пакетов, открывающий доступ к огромной базе дополнительных библиотек, что упрощает процесс разработки, в том числе запуск скрейпинг-ботов.
Начать работу с Node.js довольно просто. Он специально разработан с целью облегчить и ускорить начинающим разработчикам процесс освоения этой технологии. В конечно итоге для начала изучения достаточно иметь базовые знания JavaScript.
В этой статье содержится руководство по созданию простого проекта на Node.js, позволяющего в автоматическом режиме собирать интересующую информацию с целевого ресурса.
Создание проекта и установка зависимостей
Настройка среды разработки
Для начала работы необходимо установить сам node.js. Ссылки на руководства по установке для всех популярных ОС и актуальные версии фреймворка можно найти на официальном сайте. Проверить правильность установки можно запросом версии node.js и npm из терминала:
$ node -v v14.15.5 $ npm -v 6.14.11
Дополнительно можно подготовить удобный для вас текстовый редактор для работы с кодом. Подойдет любой, даже стандартный для вашей ОС редактор. Однако, для удобства лучше использовать один из популярных редакторов кода, например: VS Code, Sublime text, Atom или WebStorm.
Инициализация проекта
Перед тем как создать проект выберите или создайте папку, в которой будут хранится все данные необходимые для успешной работы скрейпинг-бота. После этого запустите терминал из этой папки и выполните команду создания node.js проекта:
$ npm init -y Wrote to /path.../package.json: { "name": "scrape_proj", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
Эта команда создает в вашем проекте файл package.json с описанием базовой конфигурации. Этот файл можно дополнить самостоятельно заполнив поля author, description и keywords.
Проект создан, теперь установим дополнительные библиотеки, которые помогут значительно облегчить запуск и настройку всех основных процессов, необходимых для скрейпинга. В этом примере мы будем использовать популярные библиотеки axios, cheerio и json2csv. Каждая из библиотек облегчит один из трех основных этапов, о которых будет сказано в следующей главе.
Установить новые библиотеки в node.js очень просто благодаря менеджеру пакетов, который мы уже загрузили ранее:
$ npm install axios cheerio json2csv
Эта команда создаст в вашем проекте папку node_modules, в которую будут загружены все необходимые для работы библиотек пакеты, а также обновит файл package.json, сохранив в нем информацию об установленных зависимостях.
Три этапа веб-скрейпинга
В любом проекте, в том числе при использовании node.js, реализация процесса скрейпинга состоит из трех этапов:
- Отправка HTTP-запросов к нужной странице и получение ответа от сервера;
- Парсинг нужных данных из ответа сервера;
- Сохранение полученных данных в удобной форме (файл, база данных);
Реализовать первый этап в рамках проекта нам поможет библиотека Axios. В Сети можно найти и другие решения, но Axios представляется наиболее актуальной и надежной альтернативой. С ним вы можете успешно генерировать HTTP-запросы и обрабатывать ответы от веб-серверов.
На втором этапе мы будем использовать Cheerio. Это мощная библиотека упрощающая парсинг HTML-страниц. Особенно удобно будет пользоваться тем, кто уверенно чувствует себя при работе с jQuery, потому что синтаксис для обращения к отдельным элементам веб-страницы в Cheerio и jQuery очень похож.
Третий этап при использовании JavaScript еще проще предыдущих и легко получится с помощью json2csv. С ним мы сохраним результаты парсинга в формате json в csv-файл.
Анализ страницы и выбор селекторов
Чаще всего сбор данных осуществляется с тех или иных интернет-магазинов и других сайтов онлайн-торговли. Но для пробного скрейпинга лучше всего подойдет сайт, специально созданный для проверки на нем корректности работы программ пользователей.
Среди таких сайтов можно выделить Quotes to Scrape и Books to Scrape. Эти сервисы специально созданы чтобы любой желающий мог попробовать скрейпинг без блокировок или нарушений правил сервиса.
Если вам интересно насколько легальным и этичным является веб-скрейпинг, вы можете прочитать нашу статью, посвященную этой теме.
Чтобы облегчить парсинг запрашиваемых веб-страниц удобно использовать CSS-селекторы. По ним можно гарантированно находить HTML-элемент, содержащий требуемую информацию на исследуемой странице при любом количестве вложений тэгов.
Просмотреть селектор для любого элемента в документе довольно просто. Рассмотрим для примера заголовок главной страницы Quotes to Scrape.
- Нажимаем правой кнопкой мыши на заголовок страницы, в выпадающем меню выбираем пункт “Посмотреть код”. Это откроет раздел инструментов разработчика в вашем браузере;
- В разделе Elements находим этот же заголовок внутри тега h1 и снова кликаем на него правой кнопкой мыши;
- В открывшемся меню выбираем пункт Copy;
- Среди предлагаемых опций выбираем Copy selector;
Скопированный в результате этого селектор будет выглядеть так:
body > div > div.row.header-box > div.col-md-8 > h1 > a
Использовать подобные селекторы эффективно при парсинге больших веб-страниц со сложной структурой. В некоторых других случаях можно проанализировать состав страницы и использовать более простой селектор. Например, в данном случае на главной странице есть только один h1-элемент. Поэтому работать с объемным селектором в данном случае нет необходимости.
Практические примеры скрейпинга с Node.js
Начинаем с простого: запрос заголовка
Для начала загрузим функционал внешних модулей cheerio и axios в основной файл проекта и сохраним их в переменных для дальнейшего обращения:
const cheerio = require("cheerio"); const axios = require("axios");
В другую переменную сохраним адрес целевой страницы:
const url = "https://quotes.toscrape.com/";
Для отправки GET-запроса в библиотеке Axios предусмотрен метод get(), который выполняется асинхронно, поэтому перед его вызовом необходимо поставить префикс await:
const url = "https://quotes.toscrape.com/";
В этом методе вторым аргументом можно указать дополнительные параметры запроса: http-заголовки, данные для авторизации, подключение к прокси, сетевой протокол, время ожидания ответа сервера и другое. Например, запрос через прокси выглядит так:
const response = await axios.get(url, { proxy: { "host": "proxy-IP", "port": "proxy-port", "auth": { "username": "proxy-login", "password": "proxy-password", }, } });
Чтобы получить из объекта ответа только полезную информацию - запрошенную веб-страницу без http-заголовков и статуса ответа воспользуемся возможностями библиотеки Cheerio:
const $ = cheerio.load(response.data);
Распарсить нужные данные из элемента по его селектору также очень просто:
const title = $("h1").text();>
Увидеть результат работы скрипта выведем значение переменной title в консоль:
console.log(title);
Работа с HTTP-запросами может быть сопряжена с возникновением ошибок при проблема с сервером на стороне целевого сайта, ограничении доступа к нему или нестабильным соединением со стороны node.js сервера. Поэтому создание GET-запроса необходимо обернуть в try-catch блок, которые позволит вывести в консоль информацию о типе ошибки и продолжить работу программы.
В результате общий код программы будет выглядеть так:
const cheerio = require("cheerio"); const axios = require("axios"); const url = "https://quotes.toscrape.com/"; async function getTitle() { try { const response = await axios.get(url); const document = cheerio.load(response.data); const title = document("h1").text(); console.log(title); } catch (error) { console.error(error); } } getTitle();
Код готов, осталось выполнить его с помощью Node.js:
$ node index.js
В результате выполнения программы в консоли появится заголовок сайта:
Quotes to Scrape
Поздравляем! Вы успешно написали свою первую программу на JavaScript для скрейпинга.
Получение данных о товарах
Веб-скрейпинг в большинстве случаев связан со сбором текущих цен на те или иные услуги. Для примера попробуем собрать данные о цена на книги с сайта-тренажера books.toscrape.com. Данные возьмем со страницы с книгами жанра Mystery.
Чтобы понять как получить данные о книгах с этой страницы проанализируем ее HTML-код. Объект каждой книги содержится внутри тэга <article>. Значит можно использовать Cheerio и пройтись циклом по всем данным элементам, чтобы получить требуемую информацию. Для запуска цикла воспользуемся методом each(). Упрощенно цикл будет выглядеть так:
onst books = $("article"); //Selector to get all books books.each(function () { //running a loop title = $(this).find("h3 a").text(); //extracting book title console.log(title);//print the book title });
Несложно заметить, что удобнее будет хранить целевую информацию о книгах в отдельном массиве в JSON формате пока идет выполнение цикла.
Таким образом финальный код будет выглядеть так:
const cheerio = require("cheerio"); const axios = require("axios"); const mystery = "http://books.toscrape.com/catalogue/category/books/mystery_3/index.html"; const books_data = []; async function getBooks(url) { try { const response = await axios.get(url); const $ = cheerio.load(response.data); const books = $("article"); books.each(function () { title = $(this).find("h3 a").text(); price = $(this).find(".price_color").text(); stock = $(this).find(".availability").text().trim(); books_data.push({ title, price, stock }); //store in array }); console.log(books_data);//print the array } catch (err) { console.error(err); } } getBooks(mystery);
Все, что нужно это сохранить код в отдельный файл books.js и запустить его командой node:
$ node books.js
В результате выполнения кода вся информация о книгах будет выведена в консоль. Однако, здесь мы получили книги только с одной страницы. В следующей главе мы разберемся как быть, если на сайте настроена пагинация и нужно загрузить данные с нескольких страниц сразу.
Скрейпинг страниц с пагинацией
Интернет-магазины и другие похожие по структуре веб-сайты используют пагинацию при отображении большого количества объектов. При этом логика разбиения товаров на отдельные страницы на разных сайтах может отличаться. Поэтому основной нюанс, который нужно учесть - это граничный случай последней страницы, на которой нужно остановить цикл.
Решение этой задачи довольно простое: необходимо с помощью все того же селектора проверять значение элемента содержащего ссылку на следующую страницу. Если значение ссылки не пустое, рекурсивно запускаем нашу функцию с новым URL.
Стоит помнить, что в элементе найденном при парсинге содержится относительная ссылка. Чтобы получить абсолютную ссылку для использовании ее как аргумента функции нужно добавить относительную ссылку к переменной содержащей базовый URL. В нашем коде для этого используется переменная baseUrl.
Таким образом для работы с пагинацией в цикл each() достаточно добавить новое условие:
if ($(".next a").length > 0) { next_page = baseUrl + $(".next a").attr("href"); getBooks(next_page); }
При достижении последней страницы это условие не выполнится и программа завершит свою работу. Остался последний шаг - сохранить результаты в файл!
Запись данных в CSV-файл
Выводить большие объемы данных в консоль неудобно и нецелесообразно. Потому что их не получится использовать для последующего анализа. Поэтому данные принято сохранять в файл или базу данных. Сохранить данные в CSV-файл с помощью JavaScript очень просто. Мы воспользуемся библиотеками fs и json2csv. FS установлена по умолчанию, а json2csv мы уже установили ранее.
Для использования их в нашем коде инициализируем их, сохранив их объекты в переменные:
const j2cp = require("json2csv").Parser; const fs = require("fs");
Следующие функции вызовем после выполнения основной части кода, когда данные уже получены и сохранены в массив. Их выполнение как раз создаст файл и запишет в него содержимое массива:
const parser = new j2cp(); const csv = parser.parse(books_data); fs.writeFileSync("./books.csv", csv);
В итоге наш файл books.js обновится и будет выглядеть так:
const fs = require("fs"); const j2cp = require("json2csv").Parser; const axios = require("axios"); const cheerio = require("cheerio"); const mystery = "http://books.toscrape.com/catalogue/category/books/mystery_3/index.html"; const books_data = []; async function getBooks(url) { try { const response = await axios.get(url); const $ = cheerio.load(response.data); const books = $("article"); books.each(function () { title = $(this).find("h3 a").text(); price = $(this).find(".price_color").text(); stock = $(this).find(".availability").text().trim(); books_data.push({ title, price, stock }); }); // console.log(books_data); const baseUrl = "http://books.toscrape.com/catalogue/category/books/mystery_3/"; if ($(".next a").length > 0) { next = baseUrl + $(".next a").attr("href"); getBooks(next); } else { const parser = new j2cp(); const csv = parser.parse(books_data); fs.writeFileSync("./books.csv", csv); } } catch (err) { console.error(err); } } getBooks(mystery);
Запустить его как всегда можно из консоли средствами Node.js:
$ node books.js
После успешного завершения работы программы в каталоге вашего проекта появится файл books.csv, который можно открыть любым редактором поддерживающим формат csv, например, Microsoft Excel.
Заключение
В этой статье мы привели простой пример использования языка JavaScript для скрейпинга и парсинга данных в Сети. Мы использовали фреймворк Node.js и библиотеки Axios, Cheerio и Json2csv.
Если вы хотите узнать больше о краулинге, скрейпинге и парсинге веб-сайтов, мы подготовили и другие статьи на эту тему:
- Парсинг на Python;
- Полезные советы по повышению эффективности краулинга и скрейпинга;
Комментарии