Если вам нужно разобрать HTML, регулярные выражения не подходят. В этом руководстве вы узнаете, как использовать открытый, легко изучаемый синтаксический анализатор , чтобы читать, изменять и выводить HTML из внешних источников. Используя в качестве примера nettuts, вы узнаете, как получить список всех статей, опубликованных на сайте, и отобразить их.
Кстати, вы также можете найти парсеры на Envato Market, такие как HTML5 Parser .
Шаг 1. Подготовка
Первое, что вам нужно сделать, это загрузить копию библиотеки simpleHTMLdom, которая свободно доступна на sourceforge.
В загрузке есть несколько файлов, но вам нужен только файл simple_html_dom.php; Остальные примеры и документация.
Шаг 2. Основы синтаксического анализа
Эта библиотека очень проста в использовании, но есть некоторые основы, которые вы должны рассмотреть, прежде чем приступить к работе.
Загрузка HTML
1
2
3
4
5
6
7
|
$html = new simple_html_dom();
// Load from a string
$html->load(‘<html><body><p>Hello World!</p><p>We’re here</p></body></html>’);
// Load a file
$html->load_file(‘http://net.tutsplus.com/’);
|
Вы можете создать свой исходный объект, загрузив HTML из строки или из файла. Загрузка файла может быть выполнена либо через URL, либо через локальную файловую систему.
Предупреждение: метод load_file () делегирует свою работу PHP file_get_contents. Если для allow_url_fopen в файле php.ini не задано значение true, возможно, вы не сможете открыть удаленный файл таким способом. В этом случае вы всегда можете воспользоваться библиотекой CURL для загрузки удаленных страниц, а затем прочитать их с помощью метода load ().
Доступ к информации
Получив объект DOM, вы можете начать работать с ним, используя find () и создавая коллекции. Коллекция — это группа объектов, найденных с помощью селектора — синтаксис очень похож на jQuery.
1
2
3
4
5
6
|
<html>
<body>
<p>Hello World!</p>
<p>We’re Here.</p>
</body>
</html>
|
В этом примере HTML мы рассмотрим, как получить доступ к информации во втором абзаце, изменить ее и затем вывести результаты.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
# create and load the 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>»);
# get an element representing the second paragraph
$element = $html->find(«p»);
# modify it
$element[1]->innertext .= » and we’re here to stay.»;
# output it!
echo $html->save();
|
Использование метода find () всегда возвращает коллекцию (массив) тегов, если только вы не укажете, что хотите использовать только n-й дочерний элемент, в качестве второго параметра.
Строки 2-4 : загрузить HTML из строки, как описано ранее.
Строка 7 : эта строка находит все теги <p> в HTML и возвращает их в виде массива. Первый абзац будет иметь индекс 0, а последующие абзацы будут проиндексированы соответственно.
строка 10 : получает доступ ко 2-му элементу в нашей коллекции абзацев (индекс 1) и добавляет к его атрибуту innertext. Innertext представляет содержимое между тегами, в то время как externaltext представляет содержимое, включая тег. Мы могли бы полностью заменить тег, используя externaltext.
Мы собираемся добавить еще одну строку и изменить класс нашего второго тега абзаца.
1
2
|
$element[1]->class = «class_name»;
echo $html->save();
|
В результате HTML-код команды save будет:
1
2
3
4
5
6
|
<html>
<body>
<p>Hello World!</p>
<p class=»class_name»>We’re here and we’re here to stay.</p>
</body>
</html>
|
Другие селекторы
Вот еще несколько примеров селекторов. Если вы использовали jQuery, они покажутся вам очень знакомыми.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
# get the first occurrence of id=»foo»
$single = $html->find(‘#foo’, 0);
# get all elements with class=»foo»
$collection = $html->find(‘.foo’);
# get all the anchor tags on a page
$collection = $html->find(‘a’);
# get all anchor tags that are inside H1 tags
$collection = $html->find(‘h1 a’);
# get all img tags with a title of ‘himom’
$collection = $html->find(‘img[title=himom]’);
|
Первый пример не совсем интуитивно понятен — все запросы по умолчанию возвращают коллекции, даже запрос идентификатора, который должен возвращать только один результат. Однако, указав второй параметр, мы говорим «вернуть только первый элемент этой коллекции».
Это означает, что $ single — это отдельный элемент, а не массив элементов с одним элементом.
Остальные примеры говорят сами за себя.
Документация
Полная документация по библиотеке может быть найдена на странице документации проекта .
Шаг 3. Пример из реального мира
Чтобы запустить эту библиотеку в действие, мы собираемся написать быстрый сценарий для очистки содержимого веб-сайта Nettuts и составить список статей, представленных на сайте, по заголовкам и описаниям… только в качестве примера. Соскребание — сложная область в Интернете, и ее не следует выполнять без разрешения.
1
2
3
4
|
include(‘simple_html_dom.php’);
$articles = array();
getArticles(‘http://net.tutsplus.com/page/76/’);
|
Мы начнем с включения библиотеки и вызова функции getArticles со страницей, которую мы хотели бы начать анализировать. В этом случае мы начинаем ближе к концу и будем добры к серверу Nettuts.
Мы также объявляем глобальный массив, чтобы упростить сбор всей информации о статье в одном месте. Прежде чем приступить к анализу, давайте посмотрим, как описывается краткое изложение статьи на Nettuts +.
1
2
3
4
5
6
7
8
9
|
<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. Запуск функции синтаксического анализа
1
2
3
4
5
6
7
8
|
function getArticles($page) {
global $articles;
$html = new simple_html_dom();
$html->load_file($page);
// … more …
}
|
Мы начинаем очень просто, утверждая наш глобальный объект, создавая новый объект simple_html_dom, а затем загружая страницу, которую мы хотим проанализировать. Эта функция будет вызывать себя позже, поэтому мы настроим ее на прием URL-адреса в качестве параметра.
Шаг 5. Поиск информации, которую мы хотим
1
2
3
4
5
6
7
|
$items = $html->find(‘div[class=preview]’);
foreach($items as $post) {
# remember comments count as nodes
$articles[] = array($post->children(3)->outertext,
$post->children(6)->first_child()->outertext);
}
|
Это основа функции getArticles. Нужно присмотреться, чтобы действительно понять, что происходит.
Строка 1 : создает массив элементов — div с классом предварительного просмотра. Теперь у нас есть коллекция статей, хранящихся в $ items.
Строка 5 : $ post теперь относится к одному предварительному просмотру класса. Если мы посмотрим на исходный HTML-код, то увидим, что третьим дочерним элементом является H1, содержащий заголовок статьи. Мы берем это и присваиваем его $ article [index] [0].
Не забудьте начинать с 0 и считать комментарии при попытке определить правильный индекс дочернего узла.
Строка 6 : шестым дочерним элементом $ post является <div class = «text»>. Нам нужен текст описания изнутри, поэтому мы берем внешний текст первого дочернего элемента — он будет включать тег абзаца. Отдельная запись в статьях теперь выглядит так:
1
2
|
$articles[0][0] = «My Article Name Here»;
$articles[0][1] = «This is my article description»
|
Шаг 6, Пагинация
Первое, что мы делаем, это определяем, как найти нашу следующую страницу. В Nettuts + URL-адреса легко определить, но мы собираемся притвориться, что это не так, и получим следующую ссылку с помощью анализа.
Если мы посмотрим на HTML, мы увидим следующее:
1
|
<a href=»http://net.tutsplus.com/page/2/» class=»nextpostslink»>»</a>
|
Если есть следующая страница (и не всегда будет), мы найдем якорь с классом ‘nextpostslink’. Теперь эта информация может быть использована.
1
2
3
4
5
6
7
8
|
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 Вывод результатов
Сначала мы собираемся настроить несколько основных стилей. Это совершенно произвольно — вы можете сделать вывод таким, каким пожелаете.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#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 на страницу для вывода ранее сохраненной информации.
1
2
3
4
5
6
7
8
|
<?php
foreach($articles as $item) {
echo «<div class=’item’>»;
echo $item[0];
echo $item[1];
echo «</div>»;
}
?>
|
Конечным результатом является одна HTML-страница со списком всех статей, начиная со страницы, указанной первым вызовом getArticles ().
Шаг 8 Заключение
Если вы анализируете большое количество страниц (скажем, весь сайт), это может занять больше времени, чем максимальное время выполнения, разрешенное вашим сервером. Например, запуск с моего локального компьютера занимает около одной секунды на страницу (включая время для извлечения).
На таком сайте, как Nettuts, с текущими 78 страницами учебных пособий, это может занять более одной минуты.
Из этого туториала вы должны начать разбирать HTML. Есть и другие методы для работы с DOM, включая встроенный в PHP, который позволяет вам работать с мощными селекторами xpath для поиска элементов. Для простоты использования и быстрого запуска я считаю эту библиотеку одной из лучших. В качестве заключительного замечания всегда помните, чтобы получить разрешение, прежде чем очищать сайт; это важно. Спасибо за прочтение!