Парсинг Яндекс новостей с Phantomjs

Парсинг Яндекс новостей с помощью Phantomjs

· JavaScript, Блог и Парсинг · 9 мин чтения

Меня еще с самого начала моего пути как разработчика интересовал парсинг данных. Я всегда хотел делать половину моих задач автоматически. В свое время, я работал с разными вариантами парсинга. Пробовал это делать с помощью cURL (обычным получением HTML и парсингом по классам) и используя разные библиотеки. В один момент я наткнулся на PhantomJS.

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

Я буду использовать $5 тариф от DigitalOcean. За эти деньги DO дает сервер в облачной инфраструктуре (называется Droplet). Характеристики вы можете прочитать на главной странице сайта.

Если вы сейчас читаете эту запись с Linux, то можете пропустить этапы регистрации на DO и сразу приступить  к установке Apache, PHP и PhantomJS.

P.s. Регистрируясь по ссылку выше, вы получаете $10 на ваш счет, что позволит вам пользоваться сервисом в течении 2-х месяцев бесплатно.

Создание сервера

  1. Заходим на DO и находим кнопку  «Sign Up», чтобы пройти регистрацию. Вводим почту и пароль, подтверждаем почту и заходим в панель управления.
  2. В правом верхнем углу находим кнопку «Create Droplet» и нажимаем на нее, чтобы создать сервер.
  3. Выбираем «Ubuntu 14.04.5 x64», далее $5 версию, под заголовком «Choose a datacenter region» выбираем «Frankfurt» и в самом низу страницы нажимаем на кнопку «Create», чтобы создать.

Через 5 минут вам отправят данные на почту для подключения к серверу.

Подключение к серверу

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

После запуска программы в поле «Host Name» вводите IP сервера (должно быть у вас на почту в письме). Порт оставляете предложенный программой. Далее нужно нажать на кнопку «Open». У вас должна открыться консоль где от вас потребуется ввода логина и пароля (данные брать из письма на вашей почте).

Подсказки по паролю: 

Во время ввода пароля у вас он не будет отображаться, даже звездочками. Это сделано в целям безопасности. Поэтому вводите аккуратней и вслепую или же, просто скопируйте и вставьте его. Но тут есть особенности, о ней я написал ниже.

Особенность:

Если вы скопировали пароль и пытаетесь вставить его с помощью Ctrl + C, то у вас это не получится. Сначала скопируйте пароль, после того как сервер запросит пароль нажмите правой кнопкой мыши и все что у вас было скопировано вставится в консоль.

 

Настройка сервера

Нужно установить Apache, PHP и PhantomJS.

Установка Apache

Скопируйте и вставьте каждую команду по очереди в консоль и  нажмите Enter.

sudo apt-get update
sudo apt-get install apache2

Если у вас спросят «Y/n», введите «Y» и снова нажмите Enter.

Теперь если вы зайдете на http://IP_сервера у вас должна показаться стандартная страничка Apache. На подобии того, что изображено ниже. Это означает, что установка прошло успешно.

стандартная страница Apache

Установка PHP

Так же скопируйте каждую команду отдельно, вставьте в консоль и нажмите Enter.

sudo apt-get update
apt-cache search php7
sudo apt-get install php7.0

После того как все установилось нужно создать файл, который скажет нам правильно ли установлен PHP или нет.

Для начала 100% убедимся, что мы находимся в в веб директории и удалим все стандартный файлы.

cd /var/www/html
rm -Rf *

Создадим файл test.php.

nano test.php

И напишем phpinfo() внутри PHP тегов.

<?php
        phpinfo();
?>

Сохраните все с помощью Ctrl+X, потом напишите «Y» (для подтверждения о сохранении файла) и в конце нажмите «Enter».

Теперь зайдите на http://IP_сервера/test.php . После загрузки у вас должна появиться страничка похожая на изображение ниже. Стрелкой отображается текущая PHP версия. Если у вас так и есть, то PHP успешно установлено.

установка php 7

Теперь нужно удалить этот файл, так как он больше не нужен.

rm test.php

Установка PhantomJS

Я уже описывал как устанавливать PhantomJS, поэтому советую открыть вот эту статью, сделать все что там написано и потом вернуться снова сюда.

Парсинг новостей с Яндекса

Мы будем заходить на главную страницу Яндекса, брать с шапки топ новости, вносить их в JS объект и возвращать в JSON формате.

Для начала снова перейдем в веб директорию и создадим файл yandex.js.

cd /var/www/html
nano yandex.js

Внутри пишем следующее:

'use strict';
var page = require('webpage').create();

page.settings.loadImages = false;
page.settings.resourceTimeout = 20000;
page.settings.userAgent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36';

page.open('https://yandex.ru/', function (status) {
    page.viewportSize = { width: 1024, height: 900 };

    page.injectJs('http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js');

    if (status !== "success") {
        console.log("Unable to access network");
        phantom.exit();
    } else {

        setTimeout(function() {
            var news = page.evaluate(function() {

                var _news = {};

                $('.news__list').find('li').each(function(i, value) {
                    var link = $(this).find('a');

                    var title = $.trim(link.text());
                    var url = $.trim(link.attr('href'));

                    _news[i] = {
                        title: title, 
                        url: url
                    };
                });

                return _news;
            });

            console.log(JSON.stringify(news));
            phantom.exit();
        }, 3000);
    }
});

page.onInitialized = function(){
    page.evaluate(function(){
        var isFunction = function(o) {
          return typeof o == 'function';
        };

        var bind,
          slice = [].slice,
          proto = Function.prototype,
          featureMap;

        featureMap = {
          'function-bind': 'bind'
        };

        function has(feature) {
          var prop = featureMap[feature];
          return isFunction(proto[prop]);
        }

        // check for missing features
        if (!has('function-bind')) {
          // adapted from Mozilla Developer Network example at
          // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
          bind = function bind(obj) {
            var args = slice.call(arguments, 1),
              self = this,
              nop = function() {
              },
              bound = function() {
                return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments)));
              };
            nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined
            bound.prototype = new nop();
            return bound;
          };
          proto.bind = bind;
        }
    });
};

И сохраняет — Ctrl + X, потом «Y» и нажимаем Enter.

Объяснение кода

Я выделил код с 8 по 42 строку, потому что он является самым важным.

На 8 строке, я указываю ссылку на сайт, который я хочу посетить. На 9 строке я устанавливаю размер экрана при просмотре сайта (когда PhantomJS зайдет на сайт именно такой размер экрана будет установлен).

На 11 строке, я добавляю jQuery на страницу. Далее, на 13 строке я проверяю соединение к сайту, который указали на 8 строке и если не получилось подключиться, тогда показываем ошибка в консоль и закрываем сессию.

В любом ином случае, мы ждем 3000 милисекунды (благодаря setTimeout()) и потом используем page.evaluate(), который отвечает за использование,  получение информации или манипулирование кода сайта, на который заходим. 1000 милисекунда = 1 секунда, поэтому на самом деле ждем 3 секунды для полной загрузки страницы.

На Яндекс странице все новости в шапке выводятся с помощью <ol> с классом b-news-list и уже внутри используются  <li>. Каждая <li> — это отдельная новость. Внутри <li> есть ссылка на новость.

Тем самым на 23 строке, я использую <ol> элемент с .b-news-list классом и далее ищу <li> элемент и потом с помощью метода $.each(), циклом прохожу по каждому из них и получаю нужную информацию.

На 24 строке я получаю ссылку внутри текущего <li> элемента.

На 26 строке я получаю заголовок записи из ссылки с помощью text() (jQuery) метода.

На 27 строке, я получаю атрибут href ссылки, который ведет на новость.

На 29 строке записываются данные от статьи в объект по позиции i. Всего 5 статей и тем самым запись в объекте будет от 0 до 4. Чтобы было понятнее, вы можете посмотреть на пример ответа, который находится ниже в этой записи.

На 35 строке возвращается массив с новостями и теперь он храниться в news с 19 строки. После чего на 38 строке идет вывод news в консоль, но с использованием JSON.stringify(), который делает из объекта обычную JSON строку.

Теперь попробуем получить статья из Яндекса.

Вводим в консоль следующую команду и нажимает Enter и ждем ответа в JSON формате.

phantomjs yandex.js

Пример ответа:

{"0":{"title":"Порошенко пообещал не оставить крымчан без поддержки","url":"https://news.yandex.ru/yandsearch?cl4url=lenta.ru/news/2017/02/26/helpcrimea/&lang=ru&from=main_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.glob_225.8f4a9428"},"1":{"title":"В ДНР сообщили об освобождении Донецкой фильтровальной станции","url":"https://news.yandex.ru/yandsearch?cl4url=www.kommersant.ru/doc/3227818&lang=ru&from=main_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.glob_225.e8894860"},"2":{"title":"В Москве завершился марш памяти Бориса Немцова","url":"https://news.yandex.ru/yandsearch?cl4url=ria.ru/society/20170226/1488763822.html&lang=ru&from=main_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.glob_225.a87f51bc"},"3":{"title":"Информатор ВАДА Степанова не прошла квалификацию на ЧЕ в Белграде","url":"https://news.yandex.ru/yandsearch?cl4url=rg.ru/2017/02/26/informator-vada-stepanova-ne-proshla-kvalifikaciiu-na-che-v-belgrade.html&lang=ru&from=main_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.glob_225.0d8f1180"},"4":{"title":"МИД РФ осудил варварские акции экстремистов в сирийском Хомсе","url":"https://news.yandex.ru/yandsearch?cl4url=rg.ru/2017/02/26/mid-rf-osudil-varvarskie-akcii-ekstremistov-v-sirijskom-homse.html&lang=ru&from=main_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.glob_225.c0c26571"},"5":{"title":"Въехавшему в толпу людей в Германии водителю предъявили обвинения","url":"https://news.yandex.ru/yandsearch?cl4url=www.gazeta.ru/auto/news/2017/02/26/n_9729911.shtml&lang=ru&from=reg_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.geo_96.0908c382"},"6":{"title":"Похищенные в Нигерии ученые из ФРГ освобождены","url":"https://news.yandex.ru/yandsearch?cl4url=ria.ru/world/20170226/1488767541.html&lang=ru&from=reg_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.geo_96.0eb6e019"},"7":{"title":"Спасатели в Грузии извлекли из-под лавины туриста из Сербии","url":"https://news.yandex.ru/yandsearch?cl4url=tourism.interfax.ru/ru/news/articles/39498&lang=ru&from=reg_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.geo_96.cd4e53ed"},"8":{"title":"Глава МИД ФРГ обсудит в понедельник в Вене урегулирование в Донбассе","url":"https://news.yandex.ru/yandsearch?cl4url=ria.ru/world/20170226/1488765013.html&lang=ru&from=reg_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.geo_96.f83ac812"},"9":{"title":"Ангела Меркель впервые обошла Шульца по рейтингу","url":"https://news.yandex.ru/yandsearch?cl4url=svopi.ru/politicheskaya_obstanovka_v_mire/162480&lang=ru&from=reg_portal&lr=100&msid=1488124400.65235.20941.24455&mlid=1488124057.geo_96.3710c810"}}

Это тот самый JSON топ новостей с Яндекса. Тем самым у нас все получилось.

Вывод

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

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