Меня еще с самого начала моего пути как разработчика интересовал парсинг данных. Я всегда хотел делать половину моих задач автоматически. В свое время, я работал с разными вариантами парсинга. Пробовал это делать с помощью cURL (обычным получением HTML и парсингом по классам) и используя разные библиотеки. В один момент я наткнулся на PhantomJS.
В этой записи, я покажу вам как можно парсить сайты. В примере этой записи, я буду использовать всеми известный Яндекс и конкретнее, я буду парсить (название статьи и ссылку на нее) новости (их всего пять) на главной странице.
Я буду использовать $5 тариф от DigitalOcean. За эти деньги DO дает сервер в облачной инфраструктуре (называется Droplet). Характеристики вы можете прочитать на главной странице сайта.
Если вы сейчас читаете эту запись с Linux, то можете пропустить этапы регистрации на DO и сразу приступить к установке Apache, PHP и PhantomJS.
P.s. Регистрируясь по ссылку выше, вы получаете $10 на ваш счет, что позволит вам пользоваться сервисом в течении 2-х месяцев бесплатно.
Создание сервера
- Заходим на DO и находим кнопку «Sign Up», чтобы пройти регистрацию. Вводим почту и пароль, подтверждаем почту и заходим в панель управления.
- В правом верхнем углу находим кнопку «Create Droplet» и нажимаем на нее, чтобы создать сервер.
- Выбираем «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. На подобии того, что изображено ниже. Это означает, что установка прошло успешно.
Установка 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 успешно установлено.
Теперь нужно удалить этот файл, так как он больше не нужен.
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 достаточно простой и тем более у них есть хорошая документация с примерами, которая вам поможет разобраться если у вас возникнуть какие-либо проблемы.
Единственный недостаток документации — это в том, что нет русской версии, поэтому те разработчики, которые плохо знают английский язык будет сложнее читать ее.