Пишем парсер контента на PHP. Парсинг и обработка веб-страницы на PHP: выбираем лучшую библиотеку Как парсить html страницу

контент php парсить страниц

Заметка посвящается парсингу, в частности парсинг сайтов, парсинг страниц, парсинг в веб-среде, парсинг html-контента сайта.

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

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

Давайте попробуем спарсить нужную информацию в html, попробуем достать все ссылки с нескольких страниц нашего сайта.

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

Хочу показать 2 основных способа получения контента со страницы сайта:

В первую очередь приготовим массив с нужными адресами страниц:

//3 ссылки нашего сайта: $urls = array("http://hello-site..ru/games/");

1 вариант - php функция file_get_contents . Функция возвращает html-строку, которую мы будем парсить на ссылки:

//помещаем каждую ссылку в функцию file_get_contents foreach($urls as $urlsItem){ $out .= file_get_contents($urlsItem); //и добавляем содержание каждой страницы в строку } echo $out; //здесь контент всех трех страниц

2 вариант - CURL . Библиотека, которая поддерживается php и имеет большой набор настроек, от POST-запросов до работы с FTP. Рассмотрим стандартный вызов библиотеки curl, который отдаст нам контент сайта:

foreach($urls as $urlsItem){ //пропускаем каждую ссылку в цикле $output = curl_init(); //подключаем курл curl_setopt($output, CURLOPT_URL, $urlsItem); //отправляем адрес страницы curl_setopt($output, CURLOPT_RETURNTRANSFER, 1); curl_setopt($output, CURLOPT_HEADER, 0); $out .= curl_exec($output); //помещаем html-контент в строку curl_close($output); //закрываем подключение } echo $out; //здесь контент всех трех страниц

Теперь в нашей строке $out находится контент всех трех страниц. Итак, переходим непосредственно к парсингу нашей строки.

Опять же хочу показать 3 варианта решения нашей задачи: "нативный" способ на php, с помощью встроенной библиотеки DOMDocument и библиотеки SimpleHTMLDOM.

1. php функция explode . Функция находит искомый символ или часть строки и делит целую строку на элементы массива.

Повторюсь, нам необходимо получить значения всех атрибутов href у тегов a, для этого будем делить общую строку на некоторые части\отрезки:

// explode $hrefs = explode("

Если распечатать наш массив, будет примерно следующее:

Array ( => / => /hello => /timer/ => /leftmenu/ => /faq/ => /blog/ => /web-notes/ => /ordersite/ => /games)

2. встроенная библиотека DOMDocument . Работаем с классом примерно следующим образом:

//domelement $dom = new DOMDocument; //создаем объект $dom->loadHTML($out); //загружаем контент $node = $dom->getElementsByTagName("a"); //берем все теги a for ($i = 0; $i < $node->length; $i++) { $hrefText = $node->item($i)->getAttribute("href"); //вытаскиваем из тега атрибут href } foreach($hrefText as $hrefTextItem){ //избавляемся от ссылок с пустым атрибутом href if($hrefTextItem!=""){ $clearHrefs=$hrefTextItem; } } $clearHrefs = array_unique($clearHrefs); //избавляемся от одинаковых ссылок print_r($clearHrefs); // в итоге у нас массив со всем ссылками с 3х страниц

Результат такого кода ровно такой же, что и с помощью функции explode.

3. библиотека SimpleHTMLDOM . Ее необходимо подключать из файла. Работа примерно схожа с DOMDocument. Работаем с классом:

//simplehtml include("simple_html_dom.php"); //подключаем файл с классом SimpleHTMLDOM $html = new simple_html_dom(); //создаем объект $html->load($out); //помещаем наш контент $collection = $html->find("a"); //собираем все теги a foreach($collection as $collectionItem) { $articles = $collectionItem->attr; //массив всех атрибутов, href в том числе } foreach($articles as $articlesItem){ $hrefText = $articlesItem["href"]; //собираем в массив значения подмассива с ключом href } foreach($hrefText as $hrefTextItem){ //избавляемся от ссылок с пустым атрибутом href if($hrefTextItem!=""){ $clearHrefs=$hrefTextItem; } } $clearHrefs = array_unique($clearHrefs); //избавляемся от одинаковых ссылок print_r($clearHrefs); // в итоге у нас массив со всем ссылками с 3х страниц

Повторюсь, результат в массив ровно такой же как и выше в двух вышеперечисленных.

Теперь, имея массив со всеми ссылками, собранными с трех страниц сайта, можно отправить ссылки в нужное русло, все зависит от задачи и фантазии. Имея такие возможности, можно спарсить большое количество данных самого разного вида информации, картинки, тексты, логи и т.д. Чужая информация в ваших руках, распоряжайтесь как вам угодно, но сами защищайтесь, хотя это невозможно)

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

В таких случаях перед программистом встает вопрос: какую из десятков библиотек выбрать? В этой статье мы постарались рассмотреть самые популярные варианты и выбрать из них лучший.

Регулярные выражения

Даже не смотря на то, что «регулярки» - это первое, что приходит на ум, использовать их для настоящих проектов не стоит.

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

Вместо «допиливания» своего регулярного выражения при каждом малейшем изменении кода рекомендуем использовать инструменты ниже - это и проще, и удобнее, и надежнее.

XPath и DOM

htmlSQL

Если вы не используете PHP, то можете ознакомится с кратким списком похожих инструментов для других языков программирования.

С недавних пор я работаю в компании ООО «Радио Сити Сахалин» в команде разработчиков и журналистов информационно-развлекательного портала «Ситисах ». Специально для футбольных фанатов на портале поддерживается раздел «Спорт » с новостями из мира футбола, турнирными таблицами и списком игроков команды ФК «Сахалин».

Сейчас портал переживает редизайн, поэтому разделом «Спорт» было поручено заняться мне. Основная моя функция в команде - вёрстка макетов нового дизайна. Иногда приходится решать и побочные задачи, дабы облегчить и без того нелёгкую работу нашего основного программиста. Сегодня я расскажу о «Микропарсере».

Ранее на портале футбольные турнирные таблицы заполнялись контент-менеджерами вручную. Известны случаи, когда результаты матчей появлялись на сайте Чемпионат.com быстрее, чем на нашем портале. Теперь же мы решили, наконец-то, сделать обновление таблиц автоматическим. Так как Чемпионат.com не предоставляет API (по крайней мере некоего открытого) для получения выводимых им турнирных таблиц, единственный выход - парсить.

Как использовать «Микропарсер»

«Микропарсер» состоит всего-навсего из одной функции - parse_site(array $sites, array $defaults = array()) . Первым аргументом передаётся массив сайтов (или страниц на одном сайте), которые необходимо распарсить, а вторым - массив настроек по умолчанию.

Массив $sites имеет следующий формат:

Array("zona_vostok" => array("url" => "http://www.championat.com/football/_russia2d/589/table/all.html", "xpath" => "some/x/path", //необязательный "xsl" => "absolute/path/to/xsl", //необязательный), "stackoverflow" => array("url" => "http://stackoverflow.com", "xpath" => "some/x/path", "transform" => false //необязательный));

Все ключи, кроме url - опциональны. В случае, если выражение XPath отсутствует, страница, указанная в значении ключа url , будет обработана полностью. Лист стилей XSL также можно подключить только в случае необходимости обработки «сырого» кода.

Обратите внимание на ключ "transform" => false . Он используется в том, случае, если массив $defaults содержит лист стилей XSL по умолчанию, но для данной страницы в трансформации нет необходимости.

Массив $defaults позволяет избежать копирования настроек в массиве $sites . Он может содержать только два ключа: xpath и xsl . Остальные ключи просто игнорируются.

Резюме

Написанный мной парсер состоит из одной функции с двумя параметрами (для частных и общих настроек), позволяет загружать страницу целиком или её отдельный фрагмент, а также, по желанию, обрабатывать результат таблицей стилей XSL.

Вначале для обхода нод я хотел использовать библиотеку вроде phpQuery или Ganon , но потом хорошенько подумал и понял, что тащить лишние зависимости не стоит - можно воспользоваться уже имеющимся, встроенным средством.

Рабочий пример

Давайте рассмотрим турнирную таблицу чемпионата России по футболу во втором дивизоне, зона «Восток».

Поскольку нам необходимо «вытащить» со страницы непосредственно турнирную таблицу, выражение XPath будет следующим: //div[@id="section-statistics"]/table

Исходная таблица содержит много мусора: атрибуты, классы, инлайновые стили. Поэтому мы преобразим её в более приятный вид с помощью листа стилей XSL со следующим содержанием:

Команда Игры Победы Ничьи Проигрыши Мячи Очки
even odd

Теперь напишем код, чтобы вывести готовую турнирную таблицу.

$results = parse_site(array("zona_vostok" => array("url" => "http://www.championat.com/football/_russia2d/589/table/all.html", "xpath" => "xpath" => "//div[@id="section-statistics"]/table", "xsl" => __DIR__."/football.xsl")); print $results["zona_vostok"];

И на выходе получим вот такой код HTML:

...
Команда Игры Победы Ничьи Проигрыши Мячи Очки
1 Луч-Энергия 20 12 6 2 30-17 42
2 Чита 20 12 5 3 28-14 41

Скачать «Микропарсер»

Вот несколько способов заполучить «Микропарсер»:

  1. Форкните на Гитхабе: git clone https://github.com/franzose/microparser.git
  2. Скачайте архив:

У многих из Вас возникают вопросы по поводу создания парсера на PHP . Например, есть какой-то сайт, и Вам необходимо получить с него контент. Я долго не хотел писать эту статью, поскольку конкретного смысла в ней нет. Чтобы сделать парсер на PHP , нужно знать этот язык. А те, кто его знает, такой вопрос просто не зададут. Но в этой статье я расскажу, как вообще создаются парсеры, а также, что конкретно нужно изучать.

Итак, вот список пунктов, которые необходимо пройти, чтобы создать парсер контента на PHP :

  1. Получить содержимое страницы и записать его в строковую переменную. Наиболее простой вариант - это функция file_get_contents() . Если контент доступен только авторизованным пользователям, то тут всё несколько сложнее. Здесь уже надо посмотреть, каков механизм авторизации. Далее, используя cURL , отправить правильный запрос на форму авторизации, получить ответ и затем отправить правильные заголовки (например, полученный идентификатор сессии), а также в этом же запросе обратиться к той странице, которая нужна. Тогда уже в этом ответе Вы получите конечную страницу.
  2. Изучить структуру страницы. Вам нужно найти контент, который Вам необходим и посмотреть, в каком блоке он находится. Если блок, в котором он находится не уникален, то найти другие общие признаки, по которым Вы однозначно сможете сказать, что если строка удовлетворяет им, то это то, что Вам и нужно.
  3. Используя строковые функции, достать из исходной строки нужный Вам контент по признакам, найденным во 2-ом пункте.

Отмечу так же, что всё это поймёт и сможет применить на практике только тот, кто знает PHP . Поэтому те, кто его только начинает изучать, Вам потребуются следующие знания:

  1. Строковые функции.
  2. Библиотека cURL , либо её аналог.
  3. Отличное знание HTML .

Те же, кто ещё вообще не знает PHP , то до парсеров в этом случае ещё далеко, и нужно изучать всю базу. В этом Вам поможет

Вы узнаете, как получить список всех статей, опубликованных на сайте.

Шаг 1. Подготовка

В первую очередь нужно скопировать библиотеку simpleHTMLdom , которая доступна на сайте

В архиве для загрузки хранятся несколько файлов, но вам нужен только один simple_html_dom.php . Все остальные файлы - это примеры и документация.

Шаг 2. Основы парсинга

Данную библиотеку очень просто использовать, но есть несколько основных моментов, которые следует изучить до того, как вы начнете приводить ее в действие.

$html = new simple_html_dom();
// Загрузка из строки
$html->load("

Hello World!

");
// Загрузка файла
$html->load_file("http://net.tutsplus.com/");

Вы можете создать исходный объект загрузив HTML либо из строки, либо из файла. Загрузка из файла может быть выполнена либо через указание URL , либо из вашей локальной файловой системы.

Примечания: Метод load_file() делегирует работу функции PHP file_get_contents . Если allow_url_fopen не установлен в значение true в вашем файле php.ini , то может отсутствовать возможность открывать удаленные файлы таким образом. В этом случае вы можете вернуться к использованию библиотеки CURL для загрузки удаленных страниц, а затем прочитать с помощью метода load() .

Доступ к информации

Как только у вас будет объект DOM, вы сможете начать работать с ним, используя метод find() и создавая коллекции. Коллекция - это группа объектов, найденных по селектору. Синтаксис очень похож на jQuery.



Hello World!


We"re Here.




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

1. # создаем и загружаем HTML
2. include("simple_html_dom.php");
3. $html = new simple_html_dom();
4. $html->load(“

Hello World!

“);
5. # получаем элемент представляющий второй параграф
6. $element = $html->find(“p“);
7. # модифицируем его
8. $element->innertext .= “ and we"re here to stay.“;
9. # Выводим!
10. echo $html->save();

Строки 2-4 : Загружаем HTML из строки, как объяснялось выше.

Строка 6 : Находим все тэги

В HTML, и возвращаем их в массив. Первый параграф будет иметь индекс 0, а последующие параграфы индексируются соответственно.

Строка 8 : Получаем доступ ко второму элементу в нашей коллекции параграфов (индекс 1), добавляем текст к его атрибуту innertext. Атрибут innertext представляет содержимое между тэгами, а атрибут outertext представляет содержимое включая тэги. Мы можем заменить тэг полностью, используя атрибут outertext.

Теперь добавим одну строку и модифицируем класс тэга нашего второго параграфа.

$element->class = "class_name";
echo $html->save();

Окончательный вид HTML после команды save будет иметь вид:



Hello World!


We"re here and we"re here to stay.



Другие селекторы

Несколько других примеров селекторов. Если вы использовали jQuery, все покажется вам знакомым.

# получаем первый найденный элемент с id=“foo“
$single = $html->find("#foo", 0);
# получаем все элементы с классом “foo“
$collection = $html->find(".foo");
# получаем все теги ссылок на странице
$collection = $html->find("a");
# получаем все теги ссылок, которые расположены внутри тега H1
$collection = $html->find("h1 a");
# получаем все теги img с title="himom"
$collection = $html->find("img");

Первый пример требует пояснений. Все запросы по умолчанию возвращают коллекции, даже запрос с ID, который должен вернуть только один элемент. Однако, задавая второй параметр, мы говорим “вернуть только первый элемент из коллекции”.

Это означает, что $single - единичный элемент, а не не массив элементов с одним членом.

Остальные примеры достаточно очевидны.

Документация

Полная документация по библиотеке доступна на .

Шаг 3. Пример из реального мира

Для демонстрации библиотеки в действии мы напишем скрипт для скрепинга содержимого сайта net.tutsplus.com и формирования списка заголовков и описания статей, представленных на сайте….только в качестве примера. Скрепинг относится к области трюков в веб, и не должен использоваться без разрешения владельца ресурса.

Include("simple_html_dom.php");
$articles = array();
getArticles("http://net.tutsplus.com/page/76/");

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

Так же объявим глобальный массив, чтобы сделать проще сбор все информации о статьях в одном месте. Прежде чем начинать парсинг взглянем, как описывается статья на сайте Nettuts+.



...


Title




Description



Так представлен основой формат поста на сайте, включая комментарии исходного кода. Почему важны комментарии? Они подсчитываются парсером как узлы.

Шаг 4. Начало функции парсинга

function getArticles($page) {
global $articles;
$html = new simple_html_dom();
$html->load_file($page);
// ... Дальше...
}

Начинаем с объявления глобального массива, создаем новый объект simple_html_dom , и затем загружаем страницу для парсинга. Данная функция будет рекурсивно вызываться, поэтому устанавливаем для нее в качестве параметра URL страницы.

Шаг 5. Находим ту информацию, которая нам нужна

1. $items = $html->find("div");
2. foreach($items as $post) {
3. # помним про учет комментариев в качестве узлов
4. $articles = array($post->children(3)->outertext,
5. $post->children(6)->first_child()->outertext);
6. }

Это суть функции getArticles . Нужно разобраться более детально, чтобы понять, что происходит.

Строка 1 : Создаем массив элементов - тег div с классом preview. Теперь у нас есть коллекция статей, сохраненная в $items .

Строка 4 : $post теперь ссылается на единичный div класса preview. Если мы взглянем в оригинальный HTML, то увидим, что третий элемент потомок - это тег H1 , который содержит заголовок статьи. Мы берем его и присваиваем $articles .

Помните о начале отсчета с 0 и учете комментариев исходного кода, когда будете определять правильный индекс узла.

Строка 5 : Шестой потомок $post - это

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

$articles = “Заголовок статьи“;
$articles = “This is my article description“

Шаг 6, Работа со страницами

первым делом нужно определить, как найти следующую страницу. На сайте Nettuts+ о номере страницы очень легко догадаться по URL, но нам нужно получать ссылку в парсинге.

Если посмотреть на HTML, то можно найти следующее:

»

Это сслыка на следующую страницу, и мы можем легко ее найти по классу ‘nextpostslink ’. Теперь эта информация может быть использована.

If($next = $html->find("a", 0)) {
$URL = $next->href;
$html->clear();
unset($html);
getArticles($URL);
}

В первой строке мы проверяем, можно ли найти ссылку с классом nextpostslink . Отметим использование второго параметра в функции find() . Таким образом мы указываем, что хотим получить первый элемент (индекс 0) в возвращаемой коллекции. $next содержит единичный элемент, а не коллекцию.

Затем мы присваиваем ссылку HREF переменной $URL. Это важно, потому, что далее мы удаляем объект HTML. Чтобы предотвратить утечку памяти в php5, текущий объект simple_html_dom должен быть очищен и разустановлен, прежде чем другой объект будет создан. Если этого не сделать, то вся доступная память может быть поглощена.

В завершение, мы вызываем функцию getArticles с URL следующей страницы. Рекурсия прерывается, когда не остается страниц для парсинга.

Шаг 7. Вывод результатов

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

#main {
margin:80px auto;
width:500px;
}
h1 {
font:bold 40px/38px helvetica, verdana, sans-serif;
margin:0;
}
h1 a {
color:#600;
text-decoration:none;
}
p {
background: #ECECEC;
font:10px/14px verdana, sans-serif;
margin:8px 0 15px;
border: 1px #CCC solid;
padding: 15px;
}
.item {
padding:10px;
}

Затем мы пишем маленькую функцию на PHP в странице для вывода предварительно сохраненной информации.

foreach($articles as $item) {
echo "

";
echo $item;
echo $item;
echo "
";
}
?>

Окончательный результат - это одна страница HTML со списком всех статей со страниц Nettuts+, начиная с той, которая была указана в первом вызове getArticles() .

Шаг 8. Заключение

Если Вы запускаете парсинг для большого количества страниц (скажем, весь сайт), то это может занять много времени. На таком сайте как Nettuts+, который имеет боле 86страниц, процесс парсинга может длиться более минуты.

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



Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: