Web scraping данных на JavaScript и Node.js через Nightmare JS

Web scraping данных на JavaScript и Node.js через Nightmare JS

Все чаще среди разработчиков web приложений используется стек с жестким разграничением пользовательского интерфейса (front end) и программно-аппаратной части сервера (back end). Для таких реализаций на стороне клиента, как правило, устанавливаются JavaScript-фреймворки, такие как AngularJS, Vue.js, React, Polymer и д.р., которые по определенным механизмам взаимодействуют с серверами для получения необходимых данных, после получения данных, а они как правило приходят постепенно, не за один запрос, «на лету» генерируют часть DOM и выводят их на экран. Тем самым мы получаем, что сайт «собирается» самостоятельно средствами JavaScript после того, как мы туда уже зашли. Если мы захотим, по старинке, получить и проанализировать DOM такого ресурса, к примеру, средствами PHP, C# или Python, то это у нас вызовет сильные трудности т.к. они не умеют запускать JS код. Для решения таких проблем есть два выхода:

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

Что бы выяснить какой путь решения задачи нам подходит, в первую очередь необходимо понять где затраченное время и усилий будет меньше.

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

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

Выбрав метод реализации, приступаем к построению проекта по анализу стороннего сервиса, а в роли «жертвы» идеально подходит новостной ресурс medium.com, который написан на AngularJS.

При выборе стека для реализации данной задачи, выбор пал на Nightmare JS, по причине его простоты и высокой функциональности.

Для его использования потребуется установить Node.js и npm. Для этого идем на оф.сайт, скачиваем и устанавливаем. 

Далее нам необходимо установить сам Nightmare и две дополнительные библиотеки — dotenv (отвечает за подключение конфигурационного файла «.env») , nightmare-real-mouse (подключает «реальную» мышь в Nightmare с 3-я событиями: click, mouseover, mousedown)

npm i dotenv && npm i nightmare-real-mouse

Теперь осталось только создать файл конфигураций и сам скрипт работы приложения. Для этого создадим файл «.env» и добавим в него основные ссылки для поиска данных:

KEY_TAB=\u0009
KEY_ENTER=\u000d
SITE=https://medium.com
ONLOAD_START=.js-homeStream .streamItem
ONLOAD_SEARCH=.js-postList
HEADER_SEARCH_BUTTON=label.button svg.svgIcon-use
HEADER_SEARCH_INPUT=label.button.is-touched input.js-predictiveSearchInput
HEADER_SEARCH_TEXT=From Russia
SEARCH_MAIN=div.js-postListHandle
SEARCH_LISTS=.postArticle-content
SEARCH_LIST_ITEM=.section-content h3

А теперь и сам запускаемый файл «index.js»:

//подключаем конф.файл .env
require('dotenv').config();
//подключаем приложение
const Nightmare = require("nightmare");
//подключаем реальную мышь в приложение
require('nightmare-real-mouse')(Nightmare);
//запуск асинхронно функцию приложения
(async function(conf){
    //создаем приложение с настройками
    let nightmare = Nightmare({
        show: true, //отображать приложение
        width: 1440, // ширина экрана
        waitTimeout :  0,  // в ms
    });
    try {
        const result = await nightmare
            .useragent ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36") //юзер агент
            .goto(conf.SITE) //открываемая страница
            .wait(conf.ONLOAD_START) // дожыдаемся пока появится обьект dom
            .realClick(conf.HEADER_SEARCH_BUTTON) // нажимаем кнопку в меню
            .wait(1000) // делаем задежку в секунду
            .type(conf.HEADER_SEARCH_INPUT, conf.HEADER_SEARCH_TEXT) //заполняем поле поиска
            .wait(1000) // делаем задежку в секунду
            .type('body', conf.KEY_ENTER) // нажимаем кнопку ENTER
            .wait(conf.ONLOAD_SEARCH) // дожыдаемся пока появится обьект dom
            .screenshot('shot.png') // делаем скриншот
            .evaluate(function() { //событие для получения данных
                //получаем список новостей
                let JS_SEARCH_MAIN = document.querySelector(conf.SEARCH_MAIN + ' ' + conf.SEARCH_LISTS),
                    resArr = [];
                // вытаскиваем только названия статей
                for(let key in JS_SEARCH_MAIN){
                    resArr.push( JS_SEARCH_MAIN[key].querySelector(conf.SEARCH_LIST_ITEM[key]) );
                }
                //возвращаем результат
                return resArr;
            })
            .wait(5000)  // делаем задежку в 5 секунд
            .end(); // выходим из скрипта
        console.log(result); // вывод результата в консоле
    } catch (error) {
        throw error;
    } finally {
        await nightmare.end();
    }
})(process.env);

Собственно и все, список данных получен 🙂

Опубликовано:
Пожертвование
На развите проекта, продление хостинга и на корм бобику - Дикуше :)