Автоматически или вручную?

Раньше, когда у меня возникало желание поупражняться в создании веб-сайтов, я просто заходил на веб-страницу, открывал консоль и пытался извлечь нужный мне контент – я поступал так в течение довольно длительного времени только для того, чтобы избежать набора условного, обычно бессмысленного текст-заполнителя, вставляемого в макет вэб-страницы.

Несколько месяцев назад я услышал о веб-скрэпинге – как говорится, лучше поздно, чем никогда, правда же? Оказалось, что эта штука делает ровно то, что я пытался сделать вручную.

Теперь я попытаюсь объяснить Вам как веб-скрэпить с помощью 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