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

Обратите внимание! Некоторые веб-сайты запрещают использование парсеров, поэтому перед парсингом убедитесь, что это разрешено. И даже в этом случае постарайтесь сделать это с минимальным воздействием (выполнять много HTTP-вызовов каждые несколько секунд, вероятно, не очень приятно, да?).

Что такое веб-скрапинг

Скажем, вам нужна информация с какого-то сайта, например, список статей со ссылками и авторами. На старых сайтах обычно нет API, чтобы помочь вам с этой задачей, поэтому вы можете сделать это вручную, скопировав/вставив, или вы можете автоматизировать процесс с помощью программы. Вот и все — парсер.

То, что нам нужно

Как следует из описания выше, для достижения нашей цели нам потребуется:

  1. получить HTML страницы;
  2. разбираем HTML и собираем интересующие нас данные.

Npm может предоставить нам множество пакетов для обеих задач.

Request — это известный пакет для http-запросов, и он может подойти вам идеально — если запрашиваемая страница имеет кодировку, поддерживаемую Node (то есть utf8, ascii, binary, hex, ucs2, base64). Если кодировка не поддерживается, запрос вернет вам некоторую абракадабру (конечно, это можно исправить с помощью пакетов, которые занимаются кодировкой). Needle — еще один хороший пакет для http-вызовов, и с ним может быть проще работать, если ваша страница имеет нетривиальную кодировку (например, кодировку windows-1251 для кириллицы).

Cheerio — довольно популярный пакет для DOM-парсинга, и поскольку он реализует подмножество ядра jQuery, с ним легко начать работать. Кроме того, вы можете проанализировать полученный HTML-код с помощью регулярных выражений, но это может быть довольно сложно.

Итак, настройка

Так как мы делаем самый простой скребок, мы постараемся свести наши инструменты к минимуму. В папке нашего проекта нам нужно сделать только один файл — файл для всего нашего кода. Скажем, мы назовем его app.js (или вы можете дать ему любое другое имя).

Затем давайте создадим package.json для наших зависимостей. С помощью вашего интерфейса командной строки (git bash или что вы используете) перейдите в папку проекта и введите

npm init

Заполните предоставленную форму, как хотите, единственное, что важно здесь, это установить наш файл app.js (или как вы его назвали) в качестве основного.

Затем давайте установим зависимости. Запустите эти команды:

npm install needle --save
npm install cheerio --save

«Сохранить» — это флажок для сохранения установленных пакетов в виде зависимостей в файле package.json. Вы можете открыть этот файл и проверить, он должен выглядеть примерно так:

{
  "name"         : "web-scraper",
  "version"      : "0.0.1",
  "description"  : "my web-scraper",
  "main"         : "app.js",
  "author"       : "me",
  "dependencies" : {
    "cheerio"    : "^0.22.0",
    "needle"     : "^1.4.3"
 }

Теперь наша настройка завершена, пора кодировать!

Требование необходимых модулей

Давайте начнем писать код в нашем файле app.js. Во-первых, мы должны потребовать все модули, которые нам понадобятся для парсинга:

var needle = require('needle');
var cheerio = require('cheerio');

Теперь у нас есть доступ к нашим загруженным пакетам. Однако мы, вероятно, хотим записать полученную информацию в какой-нибудь файл, а не просто показать ее в консоли. Итак, давайте потребуем модуль Node fs:

var fs = require('fs');

…и это все.

Получение HTML

Простой http-вызов — довольно тривиальная задача как для request, так и для needle. Здесь я использую needle, но с request все очень похоже.

needle.get(URL, function(err, res){
  if(err) throw err;
  console.log('Status code: ' + res.statusCode);
});

Чтобы проверить, работает ли это, замените URL-адрес каким-либо реальным адресом (например, https://www.google.com) и запустите свой код с помощью команды

node app.js

Если все прошло нормально, вы получите код состояния 200. Если запрошенная вами страница не найдена (чтобы проверить, попробуйте указать вашей программе неверный адрес, например «welovecarrots.club»), консоль зарегистрирует код состояния 404. Если вы хотите посмотреть фактический HTML , добавьте эту строку:

console.log(res.body); //will show all html body

Для нашего простого парсера достаточно кода для http-вызова. В некоторых случаях вам может понадобиться выполнить более сложные вызовы с указанными заголовками, параметрами, опциями — но это если у вашей нужной страницы есть аутентификация, сложный адрес строки запроса и т. д. И здесь все может стать действительно сложным.

Разбор HTML

Во-первых, нам нужно сохранить полученный HTML в какую-то переменную. Поскольку мы используем cheerio, мы сделаем это именно так, как они предлагают:

needle.get(URL, function(err, res){
  if(err) throw err;
  console.log('Status code: ' + res.statusCode);
  
  var $ = cheerio.load(res.body);
  console.log($); //optional - to check html body
}

Теперь, если мы загрузим неанглоязычную страницу или страницу с кучей разных символов и выполним console.log тело… о, о, все эти вещи зашифрованы, и мы ни хрена не поймем из нашей консоли. Это связано с тем, что по умолчанию cheerio декодирует сущности. Если мы хотим изменить это, мы можем добавить параметр для функции загрузки cheerio:

needle.get(URL, function(err, res){
  if(err) throw err;
  console.log('Status code: ' + res.statusCode);
  
  var $ = cheerio.load(res.body, {decodeEntities: false});
  console.log($); //to check html body
}

В любом случае, теперь мы можем начать парсить наш HTML. С cheerio это похоже на создание/изменение элементов с помощью jQuery, только теперь нам нужно анализировать DOM вместо того, чтобы создавать его. В любом случае мы начнем с просмотра оригинального DOM (используйте любой инструмент разработки, какой захотите). Нам нужно найти способ ориентироваться только на те элементы, которые нас интересуют.

В лучшем случае ваш элемент будет иметь класс. Тогда его очень легко поймать!

var title = $('.title').text();
console.log(title);

Но, может быть, у вас много заголовков и вы хотите перебрать их все... Это тоже просто:

$('h3').each(function(){
  console.log($(this).text());
}

Если вы анализируете HTML, в котором нет классов, которые помогут вам, cheerio имеет множество полезных функций для решения этой проблемы: children(), parent(), next (), prev(), nextUntil() и так далее. Если вам трудно найти нужный элемент, обязательно прочитайте документацию, это действительно полезно. Имейте в виду, что text() и html() работают совершенно по-разному: text() даст вам «текстовое содержимое каждого элемента в наборе совпадающих элементов», а html() даст «строку содержимого html». из первого выбранного элемента».

Вывод

Всегда забавно видеть результаты работы программы в консоли, но если нам действительно нужна эта очищенная информация, она нам обычно понадобится в файле — в основном в .json, иногда, может быть, в .txt. С Node.js это простая задача. Во-первых, нам потребуются два простых модуля от самого Node (их не нужно скачивать через npm или yarn).

var fs = require('fs');                       
var util = require('util');

Теперь все, что нам нужно, это переменная для хранения наших данных. Поскольку мы часто хотим, чтобы это было в формате json, нам придется построить структуру указанного json (сделать какую-то «схему»), а затем отправлять данные по мере их сбора. А затем сохраните на жесткий диск с помощью одной строки кода:

fs.writeFileSync('./myData.json', util.inspect(JSON.stringify(json)) , 'utf-8');