Парсинг сайтов при помощи Node.js: краткое руководство с примерами

парсинг сайтов

В этой статье мы познакомимся с парсингом сайтов (web scraping), который можно использовать, например, для пополнения базы email-адресов, создания сводки новостных лент, сравнения цен на один продукт среди нескольких коммерческих ресурсов или извлечения данных из поисковых машин.

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

Как это работает?

Примерно так: парсер посылает странице get-запрос, получает данные в виде HTML / XML и извлекает их в желаемом формате. Для загрузки файлов через консоль подходит утилита WGET, но можно выбрать и любой другой подходящий инструмент на просторах Сети.

Мы будем использовать написанный для Node.js программный пакет osmosis, включающий селектор css3/xpath и небольшой http-обработчик. Есть и другие фреймворки вроде Webdriver и CasperJS, но в данном случае они нам не понадобятся.

Настраиваем проект

  1. Устанавливаем Node.js, поставляемый с менеджером пакетов npm.
  2. Создаём новую папку, например, webscrap.
  3. Переходим в неё: cd webscrap.
  4. Запускаем из консоли npm init для создания файла package.json.
  5. Запускаем npm i osmosis --save, чтобы установить пакет для парсинга. Дополнительных зависимостей, кроме как от обработчика и селектора, у него не будет.
  6. Открываем package.json и создаём новый стартовый скрипт для последующего запуска команды npm start.

Итоговый package.json будет выглядеть примерно так:

{
      "name": "webscrap",
      "version": "1.0.0",
      "main": "index.js",
      "scripts": {
        "start": "node index"
      },
      "dependencies": {
        "osmosis": "^1.1.2"
      }
}

Создаём файл index.js, в нём будем делать всю работу.

Парсим информативный заголовок в Google

Это самый базовый пример, с помощью которого мы познакомимся с пакетом и запустим первый Node-скрипт. Помещаем код ниже в файл index.js и запускаем из консоли команду npm start. Она выведет заголовок веб-страницы:

const osmosis = require('osmosis');
osmosis
    .get('www.google.com')
    .set({'Title': 'title'})   // альтернатива: `.find('title').set('Title')`
    .data(console.log)  // выведет {'Title': 'Google'}

Разберём, что делают методы. Первый метод get получает веб-страницу в сжатом формате. Следующий метод set выберет элемент заголовка, представленный в виде css3-селектора. Наконец, метод data с console.log обеспечивают вывод. Метод set также принимает строки в качестве аргумента.

Получаем релевантные результаты в Google

Допустим, мы хотим получить результаты по ключевому слову analytics. Делаем следующее:

osmosis
    .get('https://www.google.co.in/search?q=analytics')
    .find('#botstuff')
    .set({'related': ['.card-section .brs_col p a']})
    .data(function(data) {
        console.log(data);
    })

Вот и всё. Этот код извлечёт все соответствующие ключевые слова с первой страницы результатов поиска, поместит их в массив и запишет в лог в консоли. Логика, стоящая за этим, такова: мы сначала анализируем веб-страницу через инструменты разработчика, проверяем блок, в котором находится слово (в данном случае это div #botstuff), и сохраняем его в массив через селектор .card-section .brs_col p a, который найдёт все соответствующие ключевые слова на странице.

Увеличиваем количество страниц при релевантном поиске

Для этого нужно добавить цепочку вызовов (chaining method), вычислив атрибут href у тега anchor (<a>). Мы ограничимся пятью страницами, чтобы Google не посчитал нас за бот. Если необходимо выставить время между парсингом соседних страниц, добавляем метод .delay(ms) после каждого .paginate().

osmosis
   .get('https://www.google.co.in/search?q=analytics')
   .paginate('#navcnt table tr > td a[href]', 5)
   .find('#botstuff')
   .set({'related': ['.card-section .brs_col p a']})
   .data(console.log)
   .log(console.log) // включить логи
   .error(console.error) // на случай нахождения ошибки

Парсим адреса электронной почты с сайта Shopify

В данном случае мы будем собирать email-адреса и названия всех приложений, последовательно перемещаясь с помощью метода .follow, и потом помечать необходимые селекторы в консоли разработчика:

osmosis
   .get('http://apps.shopify.com/categories/sales')
   .find('.resourcescontent ul.app-card-grid')
   .follow('li a[href]')
   .find('.resourcescontent')
   .set({
       'appname': '.app-header__details h1',
       'email': '#AppInfo table tbody tr:nth-child(2) td > a'
    })
   .log(console.log)   // включить логи
   .data(console.log)

Код выше можно скомбинировать с методом .paginate, чтобы собрать полностью весь контент (но при этом нас могут и заблокировать).

Теперь нужно сохранить данные в файле, сделать это можно так (пример модификации кода выше, сохранение в формате json):

const fs = require('fs');
let savedData = [];
osmosis
   .get(..).find(..).follow(..).find(..)
   .set(..)
   .log(console.log)
   .data(function(data) {
      console.log(data);
      savedData.push(data);
   })
   .done(function() {
      fs.writeFile('data.json', JSON.stringify( savedData, null, 4), function(err) {
        if(err) console.error(err);
        else console.log('Data Saved to data.json file');
      })
   });

Вот мы и закончили с основами, продолжайте экспериментировать. Но, пожалуйста, не используйте полученные знания во вред другим пользователям Сети.

Перевод статьи «Web Scraping in Node.js with Multiple Examples»