Автоматически или вручную?
Раньше, когда у меня возникало желание поупражняться в создании веб-сайтов, я просто заходил на веб-страницу, открывал консоль и пытался извлечь нужный мне контент – я поступал так в течение довольно длительного времени только для того, чтобы избежать набора условного, обычно бессмысленного текст-заполнителя, вставляемого в макет вэб-страницы.
Несколько месяцев назад я услышал о веб-скрэпинге – как говорится, лучше поздно, чем никогда, правда же? Оказалось, что эта штука делает ровно то, что я пытался сделать вручную.
Теперь я попытаюсь объяснить Вам как веб-скрэпить с помощью Node.js.
Подготовка
Мы будем использовать три пакета.
- Axios «обеспечивает основу HTTP клиента для браузера и node.js» и мы воспользуется им, чтобы получить html-код из любого выбранного нами вэб-сайта.
- Cheerio похож на jQuery, но предназначен для сервера. Мы будем его использовать для извлечения контента из html-кода, полученного с помощью Axios.
- fs – модуль узла, который мы будем использовать для записи извлеченного контента в JSON-файл.
Давайте настроим проект. Сначала создадим папку (директорию, каталог), затем войдем в нее с помощью команды терминала cd.
Для инициализации проекта просто запустите npm init
и следуйте шагам (вы можете просто нажимать enter на каждом шаге). Когда установка инициализации завершится, вы получите файл package.json
.
Теперь нам нужно установить два пакета, упомянутых выше.
npm install --save axios cheerio
(Помните, fs
уже есть часть узла, нам ничего не нужно устанавливать)
Вы увидите, что упомянутые выше пакеты установлены в директории node_modules
, они также находятся в списке файла package.json
.
Получение контента из dev.to
Ваш dev.to профиль находится на https://dev.to/<username>
. Наша цель – получить записанные там посты и сохранить их в файл json, следующим образом:
Создайте файл JavaScript в вашей директории проекта, назовите его devtoList.js
.
Требуемые пакеты установлены.
let axios = require('axios'); let cheerio = require('cheerio'); let fs = require('fs');
Теперь давайте извлечем контент из dev.to
.
axios.get('https://dev.to/aurelkurtula') .then((response) => { if(response.status === 200) { const html = response.data; const $ = cheerio.load(html); } }, (error) => console.log(err) );
В первой строке находится определенные URL. Как уже упоминалось, axios
надежная основа, затем (then
) мы проверим корректность отклика, и получим данные.
Если вы просмотрите с помощью консоли лог-файл response.data
, то увидите, что html-разметку по этому адресу. Когда мы загрузим этот html в cheerio
(jQuery сделает это за нас в фоновом режиме). Для пущей наглядности давайте заменим response.data
на жестко закодированный html:
const html = '<h3 class="title">I have a bunch of questions on how to behave when contributing to open source</h3>' const h3 = cheerio.load(html) console.log(h3.text())
Этот код вернет строку без тэга h3
.
Отбор контента
На данном этапе вы можете зайти через консоль на вэб-сайт, с которого вы хотите найти и извлечь нужный контент. Это будет выглядеть так:
Исходя из вышеизложенного вы уже знаете, что каждая статья относится к классу single-article
. Заголовок форматирован тэгом h3
и тегами внутри класса tags
.
axios.get('https://dev.to/aurelkurtula') .then((response) => { if(response.status === 200) { const html = response.data; const $ = cheerio.load(html); let devtoList = []; $('.single-article').each(function(i, elem) { devtoList[i] = { title: $(this).find('h3').text().trim(), url: $(this).children('.index-article-link').attr('href'), tags: $(this).find('.tags').text().split('#') .map(tag =>tag.trim()) .filter(function(n){ return n != "" }) } }); } }, (error) => console.log(err) );
Приведенный выше код очень легко прочесть, особенно, если вернуться к приведенному выше скриншоту. Мы проходимся по каждому узлу классом .single-article
. Затем находим только h3
, получаем из него текст и просто обрезаем trim()
избыточное пустое пространство. Затем также просто получаем URL-адрес, просто получая href
из соответствующего тега привязки.
Получение тегов действительно просто. Сначала получаем их всех как строку (#tag1 #tag2
), затем разбиваем эту строку (в местах, где встречается символ (#
) и формируем из ее частей массив. Наконец, мы просматриваем каждое значение в массиве, чтобы обрезать trim()
пустое пространство, и наконец, отфильтровываем все пустые значения (которые образовались из-за проводимого ранее тримминга (обрезки)).
Объявление пустого массива (let devtoList = []
) вне цикла позволяет нам заполнять его изнутри.
Это то, что надо. Объект массива devtoList
содержит данные, которые мы извлекли из веб-сайта. Теперь нужно просто сохранить эти данные в файле JSON, чтобы позднее воспользоваться контентом.
axios.get('https://dev.to/aurelkurtula') .then((response) => { if(response.status === 200) { const html = response.data; const $ = cheerio.load(html); let devtoList = []; $('.single-article').each(function(i, elem) { devtoList[i] = { title: $(this).find('h3').text().trim(), url: $(this).children('.index-article-link').attr('href'), tags: $(this).find('.tags').text().split('#') .map(tag =>tag.trim()) .filter(function(n){ return n != "" }) } }); const devtoListTrimmed = devtoList.filter(n => n != undefined ) fs.writeFile('devtoList.json', JSON.stringify(devtoListTrimmed, null, 4), (err)=> console.log('File successfully written!')) } }, (error) => console.log(err) );
Исходный объект массива devtoList
может иметь пустые значения, поэтому мы просто обрезаем их, а затем используем модуль fs
для записи в файл (выше, я назвал его devtoList.json
, содержание которого в виде объекта массив преобразовано в JSON.
И это все, что нужно!
Весь приведенный код можно найти в github.
Наряду с извлечением информации с помощью dev.to и приведенного выше кода, я также извлекал книги из goodreads, фильмы из IMDB – использованный для этих целей код вы также найдете в репозитории.
Перевод статьи aurel kurtula: Introduction to web scraping with Node.js