Нано-агрегатор новостей. Учимся парсить сайты с помощью phpQuery
Иногда приключается необходимость вытащить с какого-то сайта контент: изображения, ссылки или тексты. Говоря по-научному, распарсить сайт. В благих целях делать это или нет, дело Ваше. Я лишь хочу продемонстрировать, как это работает с технической точки зрения.
Для примера рассмотрим простую, уже довольно старую, но надежную библиотеку для парсинга - phpQuery. А чтобы было не скучно просто читать методы библиотеки, то запилим маленький проектик, который громко назовем "Агрегатор новостей".
В качестве примера возьмем новости футбола. А брать их будем с сайта bombardir.ru, на который я иногда заглядываю. Будем вытаскивать у них главные новости и статьи из центральной колонки и отображать их на нашей демо страничке (в целом, новостные агрегаторы так и работают). Посмотрите на исходный сайт, потом что получилось у нас, и если Вам интересно, как вытащить нужный контент, читайте статью дальше.
Идея агрегатора новостей
Посмотрите на главную страницу футбольного сайта.
Нас интересуют 2 момента: главные новости (1) и статьи из центральной колонки (2).
Мы хотим вытаскивать их с этого сайта и отображать у себя в нужном виде - вот так.
Давайте допустим, что нам нужно именно так :-)
В главных новостях нам понадобится заголовок новости и ссылка на нее. Из статей можно вытащить инфы побольше: заголовок, ссылку, картинку и описание.
Ссылки будут вести на bombardir.ru, заморачиваться и перетаскивать содержимое новости к себе в тестовом проекте мы не будем. Хотя к концу урока Вы легко сможете это сделать сами.
И конечно же, эти новости и статьи будут парситься динамически. То есть, открыв демо-страницу, мы увидим актуальный контент - все то же самое, что и выводится на бомбардире.
Итак, идею обозначили, теперь немного о библиотеке парсинга.
Библиотека phpQuery. Основы.
Скачать библиотеку можно на гитхабе или найти в исходниках проекта. Это один-единственный php-файл.
Смысл либы очень простой: на основе html-кода страницы создается объект, в котором разными методами можно проводить манипуляции с dom-элементами. Да, именно в php-коде. Можно искать нужные элементы, добавлять новые узлы, менять их местами. В общем, практически все, что позволяет делать jQuery на клиенте. Библиотеки очень похожи в плане идеологии, названия многих методов и селекторов совпадают. Это мы увидим чуть ниже, когда будем писать парсер.
Две вещи, которые нам нужно знать для работы. Первое - создание объекта (документа) phpQuery
$doc = phpQuery::newDocument($html);
И второе, как вытащить элементы по нужным селекторам
// Главные статьи $articlesItems = $doc->find('.soc-main-article');
По сути все. Селекторы, как видим, такие же, как и в привычном css или jQuery.
Чек-лист того, что нужно сделать.
Перечислим, какие пункты нам нужно последовательно выполнить.
- — подключить библиотеку phpQuery
- — получить html-код главной страницы с bombardir.ru
- — на основе полученного кода создать объект phpQuery
- — вытащить из этого объекта данные о главных новостях и записать в массив
- — аналогично со статьями из центральной колонки
- — передать полученные данные в шаблон, чтобы не мешать логику и представление
Создаем проект и подключаем библиотеку phpQuery
В корень проекта закиньте файл библиотеки phpQuery.php и создайте рядом index.php. Теперь нужно в index.php подключить библиотеку, это самое простое.
include './phpQuery.php';
Получаем html-код главной страницы с bombardir.ru
Тоже несложно.
// Общие данные $site = 'https://bombardir.ru'; $protocol = 'https:'; $html = file_get_contents($site);
Заодно создали переменные $site и $protocol - они понадобятся при формировании ссылок.
Создаем объект phpQuery
Спойлер, как это делается, уже был.
// Документ phpQuery $doc = phpQuery::newDocument($html);
Вытаскиваем данные о главных новостях
Прежде чем, засучив рукава, погрузиться код, нужно разобраться, по каким селекторам будем вытаскивать нужную информацию. Посидев в инспекторе кода на сайте bombardir, мы выясним, что блок с главными новостями находится в контейнере с классом .soc-news. Причем этих контейнеров на главной странице несколько, а нужные нам новости находятся в первом по счету.
Каждая новость находится в блоке со ссылкой .soc-text a. Из этой ссылки мы вытащим и заголовок новости, и саму ссылку. Теперь знаем, что именно нам нужно, пишем код
// Главные новости $newsItems = $doc->find('.soc-news:first li'); // Перебираем новости, вытаскиваем заголовки и ссылки $news = array(); foreach ($newsItems as $newsItem) { // Находим нужный элемент $newsElem = pq($newsItem)->find('.soc-text a'); // Вытаскиваем атрибуты $title = $newsElem->text(); $link = $newsElem->attr('href'); // Добавляем url сайта при необходимости if (strpos($link, $site) === false) { $link = $site . $link; } // Сохраняем результаты в массив array_push($news, array( 'title' => $title, 'link' => $link )); }
Разбираемся. Сначала в переменную $newsItems заносим элементы li новостей. Обратите внимание на селектор .soc-news:first. Все же некоторые отличия от синтаксиса на клиенте есть - в css используется свойство :first-child.
Затем создаем пустой массив $news, в который запишем нужные данные.
В цикле на каждый элемент li создаем объект $newsElem. Функция pq нужна, чтобы мы могли у только что созданного объекта вызывать find-методы. Затем вытаскиваем заголовок и ссылку в $title и $link и производим небольшую манипуляцию со ссылкой. Суть в том, что иногда на бомбардире ссылка дается абсолютная, иногда относительная. Нам же всегда нужны абсолютные ссылки, поэтому стоит проверить наличие строки домена в ссылке и если такового нет, дописать вручную.
И в конце закинем полученные данные в ассоциативный массив $news и проделаем ровно то же самое со статьями из центральной части сайта.
Вытаскиваем статьи из центральной колонки
Алгоритм работы ровно такой же, как и с новостями. Только в статьях у нас добавляются картинки и описания статей. Думаю, проговаривать заново смысла нет, поэтому сразу код.
// Главные статьи $articlesItems = $doc->find('.soc-main-article'); // Перебираем статьи, вытаскиваем картинки, заголовки, тексты и ссылки $articles = array(); foreach ($articlesItems as $articleItem) { // Находим нужный элемент $articleElem = pq($articleItem); // Вытаскиваем данные $image = $articleElem->find('.soc-cover-img')->attr('src'); $title = $articleElem->find('.soc-article-name')->text(); $text = $articleElem->find('.soc-article-text a')->text(); $link = $articleElem->find('.soc-article-text a')->attr('href'); // Добавляем протокол или url сайта при необходимости if (strpos($image, $protocol) === false) { $image = $protocol . $image; } if (strpos($link, $site) === false) { $link = $site . $link; } // Сохраняем результаты в массив array_push($articles, array( 'image' => $image, 'title' => $title, 'text' => $text, 'link' => $link )); }
В случае с картинкой проверяем и дописываем протокол, потому что бомбардир указывает адреса картинок, начиная с двух слэшей.
Теперь у нас в двух массивах $news и $articles есть все данные, о которых мы договаривались. Осталось их отобразить в более-менее приличном виде.
Создаем шаблон
Создадим в корне проекта пока пустой файл template.php и подключим его в index.php
include './template.php';
С index.php закончили, открываем template.php.
Там будет обычный html-код с php-вставками.
Содержимое файла с небольшой смесью php и html-кода, по традиции, картинкой
(для копипасты кода просто скачайте исходники)
Это обычный html-файл. Таблица из двух колонок: новости и статьи. В циклах перебираются соответствующие массивы и выводятся нужные данные из них. В css/main.css подключены стили, чтобы выглядело не так страшно. Впрочем, они особо никому не интересны, а если будете копипастить, то можно прямо здесь.
И на этом работа закончена!
Что в итоге?
У нас получился довольно простой, но забавный агрегатор футбольных новостей. phpQuery оказалась очень легкой в освоении. Конечно, мы затронули очень малую ее часть, но для парсинга любого сайта этого достаточно.
Вариантов применения полученной информации масса.
- — собирать новости с какого-нибудь mail.ru (идея не новая, но все же)
- — горячие акции и скидки в интернет-магазинах (на этом вообще стартапы мутят)
- — курс валют прямо с главной страницы яндекса (адекватные люди, конечно, возьмут открытую xml-ку с сайта центробанка, но всегда быть адекватными скучно)
- — и еще, и еще... Допишите свои варианты :-)
Высказывайте идеи в комментариях и подписывайтесь на рассылку, чтобы не пропустить новые статьи. И кому не лень, небольшой опрос в тему.
Истории из жизни айти и обсуждение кода.