Если вам нужно организовать парсинг HTML, то регулярные выражения мало помогут. В данном уроке вы узнаете как использовать простой парсер с открытым кодом для того, чтобы прочитать, модифицировать и использовать HTML с внешнего источника. В качестве примера будет использоваться сайт net.tutsplus.com. Вы узнаете, как получить список всех статей, опубликованных на сайте.
Шаг 1. Подготовка
В первую очередь нужно скопировать библиотеку simpleHTMLdom, которая доступна на сайте sourceforge.net.
В архиве для загрузки хранятся несколько файлов, но вам нужен только один simple_html_dom.php. Все остальные файлы - это примеры и документация.
Загрузка с Sourceforge
Шаг 2. Основы парсинга
Данную библиотеку очень просто использовать, но есть несколько основных моментов, которые следует изучить до того, как вы начнете приводить ее в действие.
Загрузка HTML
$html = new simple_html_dom();
// Загрузка из строки
$html->load('<html><body><p>Hello World!</p><p>We're here</p></body></html>');
// Загрузка файла
$html->load_file('http://net.tutsplus.com/');
Вы можете создать исходный объект загрузив HTML либо из строки, либо из файла. Загрузка из файла может быть выполнена либо через указание URL, либо из вашей локальной файловой системы.
Примечания: Метод load_file() делегирует работу функции PHP file_get_contents. Если allow_url_fopen не установлен в значение true в вашем файле php.ini, то может отсутствовать возможность открывать удаленные файлы таким образом. В этом случае вы можете вернуться к использованию библиотеки CURL для загрузки удаленных страниц, а затем прочитать с помощью метода load().
Доступ к информации
Трансформация HTML
Как только у вас будет объект DOM, вы сможете начать работать с ним, используя метод find() и создавая коллекции. Коллекция - это группа объектов, найденных по селектору. Синтаксис очень похож на jQuery.
<html>
<body>
<p>Hello World!</p>
<p>We're Here.</p>
</body>
</html>
В данном примере HTML мы собираемся разобраться, как получить доступ к информации во втором параграфе, изменить ее и затем вывести результат действий.
# создаем и загружаем HTML
include('simple_html_dom.php');
$html = new simple_html_dom();
$html->load(“<html><body><p>Hello World!</p><p>We're here</p></body></html>“);
# получаем элемент представляющий второй параграф
$element = $html->find(“p“);
# модифицируем его
$element[1]->innertext .= “ and we're here to stay.“;
# Выводим!
echo $html->save();
Строки 2-4: Загружаем HTML из строки, как объяснялось выше.
Строка 6: Находим все тэги <p> в HTML, и возвращаем их в массив. Первый параграф будет иметь индекс 0, а последующие параграфы индексируются соответственно.
Строка 8: Получаем доступ ко второму элементу в нашей коллекции параграфов (индекс 1), добавляем текст к его атрибуту innertext. Атрибут innertext представляет содержимое между тэгами, а атрибут outertext представляет содержимое включая тэги. Мы можем заменить тэг полностью, используя атрибут outertext.
Теперь добавим одну строку и модифицируем класс тэга нашего второго параграфа.
$element[1]->class = "class_name";
echo $html->save();
Окончательный вид HTML после команды save будет иметь вид:
<html>
<body>
<p>Hello World!</p>
<p class=“class_name“>We're here and we're here to stay.</p>
</body>
</html>
Другие селекторы
Несколько других примеров селекторов. Если вы использовали 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[title=himom]');
Первый пример требует пояснений. Все запросы по умолчанию возвращают коллекции, даже запрос с ID, который должен вернуть только один элемент. Однако, задавая второй параметр, мы говорим “вернуть только первый элемент из коллекции”.
Это означает, что $single - единичный элемент, а не не массив элементов с одним членом.
Остальные примеры достаточно очевидны.
Документация
Полная документация по библиотеке доступна на странице проекта.
special properties
Шаг 3. Пример из реального мира
Для демонстрации библиотеки в действии мы напишем скрипт для скрепинга содержимого сайта net.tutsplus.com и формирования списка заголовков и описания статей, представленных на сайте….только в качестве примера. Скрепинг относится к области трюков в веб, и не должен использоваться без разрешения владельца ресурса.
Скрепинг сайта Nettuts
include('simple_html_dom.php');
$articles = array();
getArticles('http://net.tutsplus.com/page/76/');
Начнем с подключения библиотеки и вызова функции getArticles с указанием страницы, с которой мы хотим начать парсинг.
Так же объявим глобальный массив, чтобы сделать проще сбор все информации о статьях в одном месте. Прежде чем начинать парсинг взглянем, как описывается статья на сайте Nettuts+.
<div class="preview">
<!-- Post Taxonomies -->
<div class="post_taxonomy"> ... </div>
<!-- Post Title -->
<h1 class="post_title"><a>Title</a></h1>
<!-- Post Meta -->
<div class="post_meta"> ... </div>
<div class="text"><p>Description</p></div>
</div>
Так представлен основой формат поста на сайте, включая комментарии исходного кода. Почему важны комментарии? Они подсчитываются парсером как узлы.
Шаг 4. Начало функции парсинга
function getArticles($page) {
global $articles;
$html = new simple_html_dom();
$html->load_file($page);
// ... Дальше ...
}
Начинаем с объявления глобального массива, создаем новый объект simple_html_dom, и затем загружаем страницу для парсинга. Данная функция будет рекурсивно вызываться, поэтому устанавливаем для нее в качестве параметра URL страницы.
Шаг 5. Находим ту информацию, которая нам нужна
Подсчет потомков
$items = $html->find('div[class=preview]');
foreach($items as $post) {
# помним про учет комментариев в качестве узлов
$articles[] = array($post->children(3)->outertext,
$post->children(6)->first_child()->outertext);
}
Это суть функции getArticles. Нужно разобраться более детально, чтобы понять, что происходит.
Строка 1: Создаем массив элементов – тег div с классом preview. Теперь у нас есть коллекция статей, сохраненная в $items.
Строка 4: $post теперь ссылается на единичный div класса preview. Если мы взглянем в оригинальный HTML, то увидим, что третий элемент потомок - это тег H1, который содержит заголовок статьи. Мы берем его и присваиваем $articles
[0].
Помните о начале отсчета с 0 и учете комментариев исходного кода, когда будете определять правильный индекс узла.
Строка 5: Шестой потомок $post - это <div class=”text”>. Нам нужен текст описания из него, поэтому мы используем outertext – в описание будет включен тег параграфа. Единичная запись в массиве статей будет выглядеть примерно так:
[PHP]$articles[0][0] = “Заголовок статьи“;
$articles[0][1] = “This is my article description“
Шаг 6, Работа со страницами
первым делом нужно определить, как найти следующую страницу. На сайте Nettuts+ о номере страницы очень легко догадаться по URL, но нам нужно получать ссылку в парсинге.
Найдем следующую страницу для парсинга
Если посмотреть на HTML, то можно найти следующее:
<a href=“http://net.tutsplus.com/page/2/“ class=“nextpostslink“>»</a>
Это сслыка на следующую страницу, и мы можем легко ее найти по классу ‘nextpostslink’. Теперь эта информация может быть использована.
if($next = $html->find('a[class=nextpostslink]', 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 в странице для вывода предварительно сохраненной информации.
<?php
foreach($articles as $item) {
echo "<div class='item'>";
echo $item[0];
echo $item[1];
echo "</div>";
}
?>
Окончательный результат - это одна страница HTML со списком всех статей со страниц Nettuts+, начиная с той, которая была указана в первом вызове getArticles().
Шаг 8. Заключение
Если Вы запускаете парсинг для большого количества страниц (скажем, весь сайт), то это может занять много времени. На таком сайте как Nettuts+, который имеет боле 86страниц, процесс парсинга может длиться более минуты.
Данный урок открывает для вас тему парсинга HTML. Существуют другие методы методы работы с DOM, которые позволяют работать с селектором xpath для поиска элементов. Описанная в данном уроке библиотека проста для использования и отлично подходит для быстрого старта. Помните, что нужно спрашивать разрешения, прежде проводить скрепинг сайта.