Статьи

Очистка экрана с Node.js

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



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

Несмотря на эти недостатки, важно немного узнать о веб-очистке и о некоторых инструментах, доступных для решения этой задачи. Когда сайт не раскрывает API или какой-либо синдикационный канал (RSS / Atom и т. Д.), Единственная возможность, с которой мы можем получить этот контент … — это очистка.

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


Скреперы могут быть написаны на любом языке, правда. Причина, по которой мне нравится использовать Node, заключается в его асинхронном характере, что означает, что мой код не блокируется ни на одном этапе процесса. Я хорошо знаком с JavaScript, так что это дополнительный бонус. Наконец, есть несколько новых модулей, написанных для NodeJS, которые упрощают надежную очистку веб-сайтов (ну, настолько надежную, насколько это возможно!). Давайте начнем!


Давайте начнем с простого варианта использования: статические веб-страницы. Это ваши стандартные веб-страницы. Для них Yahoo! Язык запросов (YQL) должен выполнять эту работу очень хорошо. Для тех, кто не знаком с YQL, это SQL-подобный синтаксис, который можно использовать для согласованной работы с различными API.

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

Давайте рассмотрим каждый из них и рассмотрим, как реализовать их в NodeJS.

HTML- таблица — это самый простой способ вырезать HTML из URL. Обычный запрос с использованием этой таблицы выглядит так:

1
select * from html where url=»http://finance.yahoo.com/q?s=yhoo» and xpath=’//div[@id=»yfi_headlines»]/div[2]/ul/li/a’

Этот запрос состоит из двух параметров: «url» и «xpath». URL-адрес не требует пояснений. XPath состоит из строки XPath, сообщающей YQL, какой раздел HTML должен быть возвращен. Попробуйте этот запрос здесь .

Дополнительные параметры, которые вы можете использовать, включают в себя browser (логическое значение), charset (строка) и compat (строка). Мне не приходилось использовать эти параметры, но обратитесь к документации, если у вас есть конкретные потребности.

К сожалению, XPath не очень популярный способ обхода древовидной структуры HTML. Это может быть сложно читать и писать для начинающих.

Давайте посмотрим на следующую таблицу, которая делает то же самое, но позволяет вместо этого использовать CSS

Таблица data.html.cssselect — мой предпочтительный способ удаления HTML со страницы. Он работает так же, как HTML- таблица, но позволяет использовать CSS вместо XPath. На практике эта таблица преобразует CSS в XPath изнутри, а затем вызывает таблицу html , поэтому она немного медленнее. Разница должна быть незначительной для нужд очистки.

Обычный запрос с использованием этой таблицы выглядит так:

1
select * from data.html.cssselect where url=»www.yahoo.com» and css=»#news a»

Как видите, он намного чище. Я рекомендую сначала попробовать этот метод, когда вы пытаетесь очистить HTML с помощью YQL. Попробуйте этот запрос здесь .

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

Использование этой таблицы позволяет извлекать все содержимое HTML этой страницы в виде одной строки, а не в виде JSON, который разделен на основе структуры DOM.

Например, обычный ответ JSON, который <a> тег <a> выглядит следующим образом:

1
2
3
4
5
6
7
«results»: {
  «a»: {
    «href»: «…»,
    «target»: «_blank»,
    «content»: «Apple Chief Executive Cook To Climb on a New Stage»
   }
}

Посмотрите, как атрибуты определены как свойства? Вместо этого ответ из таблицы htmlstring будет выглядеть следующим образом:

1
2
3
4
5
«results»: {
  «result»: {
    «<a href=\»…\» target=»_blank»>Apple Chief Executive Cook To Climb on a New Stage</a>
   }
}

Итак, почему вы используете это? По моему опыту, это очень полезно, когда вы пытаетесь очистить большое количество отформатированного текста. Например, рассмотрим следующий фрагмент:

1
2
<p>Lorem ipsum <strong>dolor sit amet</strong>, consectetur adipiscing elit.</p>
<p>Proin nec diam magna.

Используя таблицу htmlstring , вы можете получить этот HTML- код в виде строки и использовать регулярные выражения для удаления тегов HTML, в результате чего у вас останется только текст. Это более простая задача, чем перебирать JSON, который был разбит на свойства и дочерние объекты на основе структуры DOM страницы.


Теперь, когда мы немного узнали о некоторых таблицах, доступных нам в YQL, давайте реализуем веб-скребок с использованием YQL и NodeJS. К счастью, это действительно просто благодаря модулю node-yql Дерека Гатрайта.

Мы можем установить модуль используя npm :

1
npm install yql

Модуль чрезвычайно прост и состоит только из одного метода: метода YQL.exec() . Это определяется как следующее:

1
function exec (string query [, function callback] [, object params] [, object httpOptions])

Мы можем использовать его, требуя его и вызывая YQL.exec() . Например, допустим, мы хотим очистить заголовки от всех сообщений на главной странице Nettuts:

1
2
3
4
5
6
7
var YQL = require(«yql»);
 
new YQL.exec(‘select * from data.html.cssselect where url=»http://net.tutsplus.com/» and css=».post_title a»‘, function(response) {
 
    //response consists of JSON that you can parse
 
});

Отличительной особенностью YQL является его способность тестировать ваши запросы и определять, какой JSON вы получаете в режиме реального времени. Перейдите в консоль, чтобы попробовать этот запрос, или щелкните здесь, чтобы увидеть необработанный JSON.

Объекты params и httpOptions являются необязательными. Параметры могут содержать такие свойства, как env (независимо от того, используете ли вы определенную среду для таблиц) и format (xml или json). Все свойства, передаваемые в params , кодируются URI и добавляются в строку запроса. Объект httpOptions передается в заголовок запроса. Здесь вы можете указать, хотите ли вы включить SSL, например.

Файл JavaScript с именем yqlServer.js содержит минимальный код, необходимый для очистки с помощью YQL. Вы можете запустить его, введя следующую команду в своем терминале:

1
node yqlServer.js

YQL — мой предпочтительный выбор для удаления контента со статических веб-страниц, потому что он легко читается и прост в использовании. Тем не менее, YQL завершится ошибкой, если на данной веб-странице есть файл robots.txt который не дает ответа на него. В этом случае вы можете посмотреть на некоторые из утилит, упомянутых ниже, или использовать PhantomJS, о которых мы расскажем в следующем разделе.

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

JSDOM — очень популярный проект, который реализует W3C DOM на JavaScript. Когда предоставляется HTML, он может создавать DOM, с которым вы можете взаимодействовать. Ознакомьтесь с документацией, чтобы узнать, как вы можете использовать JSDOM и любую библиотеку JS (например, jQuery) для очистки данных с веб-страниц.


До сих пор мы рассматривали некоторые инструменты, которые могут помочь нам очистить веб-страницы со статическим контентом. С YQL это относительно просто. К сожалению, нам часто предоставляют страницы с контентом, который динамически загружается с помощью JavaScript. В этих случаях страница часто изначально пуста, а затем содержимое добавляется позже. Как мы можем решить эту проблему?

Позвольте мне привести пример того, что я имею в виду; Я загрузил простой HTML-файл на свой собственный веб-сайт, который добавляет некоторый контент через JavaScript через две секунды после document.ready() функции document.ready() . Вы можете проверить страницу здесь . Вот как выглядит источник:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
    <head>
        <title>Test Page with content appended after page load</title>
    </head>
 
    <body>
        Content on this page is appended to the DOM after the page is loaded.
 
        <div id=»content»>
 
        </div>
 
    <script src=»http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js»></script>
    <script>
        $(document).ready(function() {
 
            setTimeout(function() {
                $(‘#content’).append(«<h2>Article 1</h2><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p><h2>Article 2</h2><p>Ut sed nulla turpis, in faucibus ante. Vivamus ut malesuada est. Curabitur vel enim eget purus pharetra tempor id in tellus.</p><h2>Article 3</h2><p>Curabitur euismod hendrerit quam ut euismod. Ut leo sem, viverra nec gravida nec, tristique nec arcu.</p>»);
            }, 2000);
 
        });
    </script>
    </body>
</html>

Теперь давайте попробуем очистить текст внутри <div id="content"> используя YQL.

1
2
3
4
5
6
7
8
var YQL = require(«yql»);
 
new YQL.exec(‘select * from data.html.cssselect where url=»http://tilomitra.com/repository/screenscrape/ajax.html» and css=»#content»‘, function(response) {
 
    //This will return undefined!
    console.log(response.results);
 
});

Вы заметите, что YQL возвращает undefined потому что, когда страница загружена, <div id="content"> пусто. Содержание еще не было добавлено. Вы можете попробовать запрос здесь для себя.

Давайте посмотрим, как мы можем обойти эту проблему!

PhantomJS может загружать веб-страницы и имитировать браузер на основе Webkit без графического интерфейса.

Мой предпочтительный метод сбора информации с этих сайтов — использовать PhantomJS . PhantomJS описывает себя как «безголовый Webkit с JavaScript API». Проще говоря, это означает, что PhantomJS может загружать веб-страницы и имитировать браузер на основе Webkit без графического интерфейса. Как разработчик, мы можем вызывать определенные методы, которые PhantomJS предоставляет для них. выполнить код на странице. Поскольку он ведет себя как браузер, сценарии на веб-странице выполняются так же, как и в обычном браузере.

Чтобы получить данные с нашей страницы, мы собираемся использовать PhantomJS-Node , отличный маленький проект с открытым исходным кодом, который связывает PhantomJS с NodeJS. Под капотом этот модуль запускает PhantomJS как дочерний процесс.

Прежде чем вы сможете установить модуль PhantomJS-Node NPM, вы должны установить PhantomJS. Однако установка и сборка PhantomJS может быть немного сложнее.

Сначала зайдите на PhantomJS.org и загрузите соответствующую версию для вашей операционной системы. В моем случае это был Mac OSX.

После загрузки распакуйте его куда-нибудь, например /Applications/ . Далее вы хотите добавить его в свой PATH :

1
sudo ln -s /Applications/phantomjs-1.5.0/bin/phantomjs /usr/local/bin/

Замените 1.5.0 вашей загруженной версией PhantomJS. Имейте в виду, что не все системы будут иметь /usr/local/bin/ . Некоторые системы будут иметь вместо: /usr/bin/ , /bin/ или usr/X11/bin .

Для пользователей Windows, ознакомьтесь с кратким руководством здесь . Вы будете знать, что все настроено, когда откроете свой Терминал и напишите phantomjs , и вы не получите никаких ошибок.

Если вам неудобно редактировать свой PATH , запишите, где вы распаковали PhantomJS, и я покажу другой способ его установки в следующем разделе, хотя я рекомендую вам изменить ваш PATH .

Настроить PhantomJS-Node намного проще. Если у вас установлен NodeJS, вы можете установить его через npm:

1
npm install phantom

Если вы не редактировали PATH на предыдущем шаге при установке PhantomJS, вы можете перейти в каталог phantom/ phantom.js npm, и отредактировать эту строку в phantom.js .

1
ps = child.spawn(‘phantomjs’, args.concat([__dirname + ‘/shim.js’, port]));

Измените путь на:

1
ps = child.spawn(‘/path/to/phantomjs-1.5.0/bin/phantomjs’, args.concat([__dirname + ‘/shim.js’, port]));

Как только это будет сделано, вы можете проверить это, запустив этот код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
var phantom = require(‘phantom’);
phantom.create(function(ph) {
  return ph.createPage(function(page) {
    return page.open(«http://www.google.com», function(status) {
      console.log(«opened google? «, status);
      return page.evaluate((function() {
        return document.title;
      }), function(result) {
        console.log(‘Page title is ‘ + result);
        return ph.exit();
      });
    });
  });
});

Запуск этого в командной строке должен вызвать следующее:

1
2
opened google?
Page title is Google

Если вы получили это, вы все готовы и готовы к работе. Если нет, оставьте комментарий, и я постараюсь вам помочь!

Чтобы вам было проще, я включил в загрузку файл JS phantomServer.js в котором для загрузки веб-страницы используется API-интерфейс PhantomJS. Он ждет 5 секунд перед выполнением JavaScript, который очищает страницу. Вы можете запустить его, перейдя в каталог и введя следующую команду в своем терминале:

1
node phantomServer.js

Я дам обзор того, как это работает здесь. Во-первых, нам требуется PhantomJS:

1
var phantom = require(‘phantom’);

Далее мы реализуем некоторые методы из API . А именно, мы создаем экземпляр страницы и затем вызываем метод open() :

01
02
03
04
05
06
07
08
09
10
11
12
phantom.create(function(ph) {
  return ph.createPage(function(page) {
 
    //From here on in, we can use PhantomJS’ API methods
    return page.open(«http://tilomitra.com/repository/screenscrape/ajax.html», function(status) {
 
            //The page is now open
            console.log(«opened site? «, status);
 
        });
    });
});

Как только страница открыта, мы можем добавить в нее немного JavaScript. Давайте page.injectJs() jQuery через метод page.injectJs() :

01
02
03
04
05
06
07
08
09
10
11
12
13
phantom.create(function(ph) {
  return ph.createPage(function(page) {
    return page.open(«http://tilomitra.com/repository/screenscrape/ajax.html», function(status) {
      console.log(«opened site? «, status);
 
            page.injectJs(‘http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js’, function() {
                //jQuery Loaded
                //We can use things like $(«body»).html() in here.
 
            });
    });
  });
});

Теперь jQuery загружен, но мы не знаем, загружен ли динамический контент на странице. Для этого я обычно помещаю свой очищающий код в функцию setTimeout() которая выполняется через определенный промежуток времени. Если вам нужно более динамичное решение, API PhantomJS позволяет прослушивать и эмулировать определенные события. Давайте рассмотрим простой случай:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
setTimeout(function() {
    return page.evaluate(function() {
 
        //Get what you want from the page using jQuery.
        //A good way is to populate an object with all the jQuery commands that you need and then return the object.
 
        var h2Arr = [], //array that holds all html for h2 elements
        pArr = [];
 
        //Populate the two arrays
        $(‘h2’).each(function() {
            h2Arr.push($(this).html());
        });
 
        $(‘p’).each(function() {
            pArr.push($(this).html());
        });
 
        //Return this data
        return {
            h2: h2Arr,
            p: pArr
        }
    }, function(result) {
        console.log(result);
        ph.exit();
    });
}, 5000);

phantomServer.js все это вместе, наш файл phantomServer.js выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var phantom = require(‘phantom’);
phantom.create(function(ph) {
  return ph.createPage(function(page) {
    return page.open(«http://tilomitra.com/repository/screenscrape/ajax.html», function(status) {
      console.log(«opened site? «, status);
 
            page.injectJs(‘http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js’, function() {
                //jQuery Loaded.
                //Wait for a bit for AJAX content to load on the page.
                setTimeout(function() {
                    return page.evaluate(function() {
 
                        //Get what you want from the page using jQuery.
                        var h2Arr = [],
                        pArr = [];
                        $(‘h2’).each(function() {
                            h2Arr.push($(this).html());
                        });
                        $(‘p’).each(function() {
                            pArr.push($(this).html());
                        });
 
                        return {
                            h2: h2Arr,
                            p: pArr
                        };
                    }, function(result) {
                        console.log(result);
                        ph.exit();
                    });
                }, 5000);
 
            });
    });
    });
});

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

1
2
3
4
5
6
7
→ node phantomServer.js
opened site?
{ h2: [ ‘Article 1’, ‘Article 2’, ‘Article 3’ ],
  p:
   [ ‘Lorem ipsum dolor sit amet, consectetur adipiscing elit.’,
     ‘Ut sed nulla turpis, in faucibus ante.
     ‘Curabitur euismod hendrerit quam ut euismod.

В этом уроке мы рассмотрели два различных способа выполнения очистки веб-страниц. При извлечении из статической веб-страницы мы можем воспользоваться YQL, который прост в настройке и использовании. С другой стороны, для динамических сайтов мы можем использовать PhantomJS. Это немного сложнее в настройке, но предоставляет больше возможностей. Помните: вы можете использовать PhantomJS для статических сайтов!

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