Побродив по нашим и англоязычным форумам, поняла, что спор о том, лучше ли парсить html регулярными выражениями или использовать для этих целей возможности PHP DOM, является холиваром. Сама же я пришла к выводу, что все зависит от сложности структуры данных. Ведь если структура достаточно сложная, то с помощью регулярок приходится парсить в несколько этапов: сначала выделить большой кусок, потом разделить его на более маленькие и т.д.. В итоге, если данные сложные (или их очень много), то процесс парсинга может значительно затянуться. Ресурсоемкость в этом случае еще будет зависеть, конечно же, от самих регулярных выражений. Если в регэкспах много ".*" (они являются самыми ресурсоемкими, т.к. "прочесывают" исходный код с максимальной жадностью), то замедление будет заметным.
И вот как раз в этом-то случае как нельзя кстати приходится PHP DOM. Это удобный инструмент для парсинга как XML, так и HTML. Некоторые придерживаются мнения, что парсить html регэкспами вообще нельзя, и яростно защищают PHP DOM.
В свою очередь я ознакомилась с этим расширением, написав простенький скрипт. Который и привожу здесь, чтобы наглядно показать, как это все легко и просто. В примере разбирается html с частью карты сайта этого блога. Он присвоен переменной прямо внутри кода. В "боевых" же условиях исходные данные следует получать, например, через file_get_contents().
<?php
$html = '
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<head>
<title>Parsing-and-i.blogspot.com Map</title>
</head>
<body>
<h2>Последние темы блога</h2>
<!-- на 09.08.2009 -->
<table border="0">
<tbody>
<tr>
<td><a href="http://parsing-and-i.blogspot.com/2009/08/blog-post_06.html" title="Базы">http://parsing-and-i.blogspot.com/2009/08/blog-post_06.html</a></td>
<td>Базы</td>
</tr>
<tr>
<td><a href="http://parsing-and-i.blogspot.com/2009/08/mysql-delphi-express.html" title="MySQL и Delphi. Express-метод">http://parsing-and-i.blogspot.com/2009/08/mysql-delphi-express.html</a></td>
<td>MySQL и Delphi. Express-метод</td>
</tr>
<tr>
<td><a href="http://parsing-and-i.blogspot.com/2009/08/blog-post.html" title="Пост о том, что лучше сто раз проверить">http://parsing-and-i.blogspot.com/2009/08/blog-post.html</a></td>
<td>Пост о том, что лучше сто раз проверить</td>
</tr>
</tbody>
</table>
</body>
</html>
';
/** создаем новый dom-объект **/
$dom = new domDocument;
/** загружаем html в объект **/
$dom->loadHTML($html);
$dom->preserveWhiteSpace = false;
/** элемент по тэгу **/
$tables = $dom->getElementsByTagName('table');
/** получаем все строки таблицы **/
$rows = $tables->item(0)->getElementsByTagName('tr');
/** цикл по строкам **/
foreach ($rows as $row)
{
/** все ячейки по тэгу **/
$cols = $row->getElementsByTagName('td');
/** выводим значения **/
echo $cols->item(0)->nodeValue.'<br>';
echo $cols->item(1)->nodeValue.'<br>';
echo '<hr>';
}
?>
В результате после запуска скрипта получаем такую картину:
Upd: Без всякого сомнения, для более удобной работы со структурой HTML в PHP вам надо познакомиться с библиотекой PHP Simple HTML DOM Parser. Я бы отдала предпочтение именно ей.
Чтобы быть в курсе обновлений блога, можно подписаться на RSS.
Подскажите пожалуйста, если важным критерием при парсинге является не производительность, а потребление памяти, что лучше использовать: регулярные выражения или парсить через DOM? Как на ваш взгляд?
ОтветитьУдалитьВАЗП, я не в курсе :)
ОтветитьУдалитьСпасибо, хороший пример для начала использования парсера ДОМа. Я лично всегда парсил все регулярными выражениями, ИМХО так проще. :)
ОтветитьУдалитьМаша, это очень долго. Может быть пример не самый удачный но, здесь нужно ровно одно регулярное выражение
ОтветитьУдалить#<a\s+href="(.+)".+<td>(.+)</td>#sUi
Спасибо за комментарий, про регулярные выражения я, конечно же, в курсе ))) Да, пример применения выбрала не самый удачный, но это просто пример) Вдруг кому-нибудь пригодится.
ОтветитьУдалитьНе БОЛЬШОЕ спасибо, хоть пример и простой но реально пригодился для построения более сложного, т.к. я раньше парсингом вообще не занимался ))..
ОтветитьУдалитьНа будущее - DOM грузит весь документ разом в память, так что если у вас огромный объем дынных, то ни DOM, ни, те более regexp, не помогут. На помощь придет SAX. Им конечно нельзя создавать или редактивароть XML, но зато он в память не берет всё разом. И да, он годится только для XML. И раз уж пошла такая пьянка, то DOM не станет разбирать совсем невалидный документ (например теги незакрытые или порядок совсем неверный). Тут на помощь придет прекрасное расширение для PHP - tidy. Почитайте про него. Исправляет любой html из полного "г.." до адекватного и валидного типа.
ОтветитьУдалитьЗдравствуйте.
ОтветитьУдалитьПодскажите пожалуйста, можно ли средствами DOM найти соответствующий закрывающий тег для заданного?
Т.е., например, мы имеем такой код:
див_Х .class=."foo"
див_01 .class=."x1".../див_01
див_02 .class=."x2".../див_02
див_03 .class=."x3".../див_03
/див_Х *- вот этот тег, например, нужно найти -*
Не получается найти номер строки закрывающего тега.
Можно ли найти закрывающий див для дива див_Х class.=."foo" средствами DOM?
Извините, пришлось наставить точек и русских букв, т.к. blogger не пропускает код.
А что с кодировкой.
ОтветитьУдалитьУ меня выдает кракозябры...
Сергей, у меня с кодировкой все нормально, непроверенные примеры не привожу обычно :) Если у вас что-то не так - поменять кодировку просто.
ОтветитьУдалитьЧтото у меня на денвере тоже с кодировкой проблемы.. Не пойму, даже если сохраняю в UTF-8 сам скрипт с примером, какую в итоге выдает кодировку скрипт, так и не смог подобрать... В опере ни одна не подошла. Тоже самое при сохранении скрипта в ANSI... Что может быть, не подскажете?
ОтветитьУдалить!Большое, !Человеческое - !Спасибо за статью...
ОтветитьУдалитьecho mb_convert_encoding($cols->item(1)->nodeValue, "", "utf-8");
ОтветитьУдалить-------------
при этом не пишите заголовок документа...
иначе, эксерементируйте...
Конвертация кодировки помогла. Спасибо.
ОтветитьУдалитьВопрос.
Как граммотно спарсить с помощью регулярных выражений блок страницы.
Просто он берет код построчно, а не целиком.
Если надо проверить все ссылки или опр тег, то это просто.
А мне надо найти ссылку. выудить из неё кое-что. Это я сделал. А потом в этом же блоке выудить из нескольких < td > еще инфу. Потом бежать к следующей ссылке.
Я примерно представляю, как это реализовать. Просто хочется спросить, как это сделать по-человечески, без нагораживания условий и тп.
Сложно ответить на этот вопрос, не имея перед глазами исходного кода страницы. Ваши слова не дают полного представления о задаче.
ОтветитьУдалитьМожет, если с данными требуется производить достаточно много манипуляций, вам стоит попробовать использовать другой, более удобный инструмент? Я недавно писала на блоге про PHP Simple HTML DOM Parser (http://parsing-and-i.blogspot.com/2010/05/php-simple-html-dom-parser.html). Скоро планирую добавить еще одну статью с примером его использования.
Удачи.
P.S. И, если не сложно, подписывайте комментарий своим именем, а то обращаться к Анонимусу — что обращаться в пустоту.
2Masha
ОтветитьУдалитьдополнение к посту от 9 июня 2010 г. 23:44
код примерно такой
< tr >
< td >< a инфа1 >< /a >< /td >
< td >инфа2< /td >
< td >инфа3< /td >
< td >инфа4< /td >
< /tr >
вот. надо выудить из множества таких блоков на странице инфу.
Просто хотелось гарантию, что инфа1 будет соответствовать инфе2. А то в построчном парсе регулярными выражениями че-нить проскочит и сдвинется. Вот, думаю как это красиво обойти, что бы случайно не потерять ключи в ссылке к информации из ячеек таблицы.
P.S. Пойду читать то, что Вы предложили.
Алексей, да, тогда та библиотека - именно то, что надо.
ОтветитьУдалитьДа, благодарю. Я уже реализовал с её помощью. Довольно простой и понятный мануал. И регулярные выражения пригодились.
ОтветитьУдалитьЗдравствуйте, Мария.
ОтветитьУдалитьМеня зовут Виктор.
Я ищу человека, профессионала в области парсинга. Судя по вашему блогу и имхо, вы таковым являетесь.
Вы можете помочь составить скрипт парсинга списков категорий на молоток.ру? Общий смысл: конечная ветка категории --> список товаров в формате: картинка, название, состояние товара, цена, ставки, время до окончания. При кликании на ссылку товара в списке, вывод его данных в редактируемую форму.
Оплата через Webmoney или Paypal.
Если возможно, хотелось бы устроить это в виде консультации по скайпу с почасовой оплатой. Также я готов заплатить за готовый скрипт.
Виктор, добрый день!
ОтветитьУдалитьСпасибо за предложение, но, к сожалению, очень много работы, заказы не принимаю.
Пример работает, все ок. Но как только попытался выдрать что-то из реального сайта, получил кучу сообщений вот такого вида:
ОтветитьУдалить"Warning: DOMDocument::loadHTML() [domdocument.loadhtml]: htmlParseEntityRef: expecting ';' in Entity line: ххх"
Гугление текста ошибки не помогает, не подскажете в чем может быть проблема?
El Coyot, невалидный html, скорее всего. Попробуйте сначала прогнать код через валидатор (tidy, например).
ОтветитьУдалитьСтатья хорошая, библиотека тоже, работает на ура, распарсил контактную форму, вставил во все поля инфо, вывел юзеру для редактирования контакта. Спасибо Маш. Все отлично.
ОтветитьУдалитьа как извлекать каждый второй и третий TR ???
ОтветитьУдалитьАнонимный, какой запрограммируешь алгоритм - то и извлечешь. Я не консультирую по азам программирования.
ОтветитьУдалитьЗдесь описано решение проблемы с кодировкой:
ОтветитьУдалитьhttp://blog.fxposter.org/2008/07/20/domdocument-encoding-in-html/
Кратко:
- new DomDocument('1.0', 'UTF-8');
- в html-файле должен присутствовать meta-тег с указанием кодировки.
Спасибо!
ОтветитьУдалитьСпасибо помогло!!! А вот такой вопрос: надо сделать копию одного tr> делаю с помощью DOMNode::appendChild, ни чего не добавляет, но скрипт корректно выполняется. Не подскажите в чем может быть проблема???
ОтветитьУдалитьSimple HTML DOM годится только для идеалного html кода страницы, иначе начинает жестко тупить. Другое дело phpparserplus.esy.es, который работает с помощью multi-curl и проверен на сотни сайтах.
ОтветитьУдалить