Как написать парсер HTML на PHP

Дык HTML парсить - это вам не мешки ворочать!Много разных задач сводятся к парсингу HTML. И не только сбор контента для ГС. Вот мне например понадобилось разобрать HTML собственного контента, чтобы культурненько облагородить. Ну например внешние ссылочки спрятать.

Сначала я подумал — фигня. Найти в тексте строчку «<a href=http://» и получи себе внешнюю ссылочку. Но ведь в контенте теги и атрибуты могут писаться с заглавной буквы. И ещё между A и HREF может стоять произвольное количество пробелов. Но самое главное, между A и HREF могут быть разные всякие другие слова. Короче выхода нет, кроме как строить нормальное дерево DOM и его анализировать. То бишь парсить.

Но оказывается библиотека парсера давно есть и встроена в PHP. Осталось только попользоваться!

Расписывать не буду. Мануал превзойти тяжело. А примерчик для направления — самый раз будет.

// Содаём объект
$dom = new domDocument; 
$dom->strictErrorChecking=false;
$dom->recover = true;

// скармливаем объекту готовую строку
$dom->loadHTML($strHTML); 
    
//или удобнее будет файл?
$dom->loadHTMLFile($htmlFile);

//И всё! Можно вытащить любой аттрибут любого элемента!
foreach($dom->getElementsByTagName('img') as $img_dom) 
    $img_src= html_entity_decode($img_dom->getAttribute('src'));

    // А вот можно переделать ссылку на свой хитрый редирект
    $link_dom->setAttribute('href','/link/redirect/'.$link->id);
}
В результате можно вывести код, который получился в результате.
echo $dom->saveHTML();

Пара слов про кириллицу
=======================
Раз уж вы читаете этот сайт на русском языке, то и парсить вероятно будете тоже русскоязычный сайт. А вот с ними не всё так просто.

В документации написано, что класс делает utf_encode, но ситуация ещё сложнее. Вот пример

loadHTML("

Германия, Великобритания и Франция

"); // Тут работает foreach($dom->getElementsByTagName('p') as $p_dom) echo utf8_decode ($p_dom->nodeValue); // А тут фигня echo utf8_decode($dom->saveHTML()); // Тут по-лучше, но всё равно фигня echo html_entity_decode($dom->saveHTML()); ?>

Вот такой получаю результат:
Screenshot_2

По этому примеру понятно, что преобразование похоже на htmlentities, но не совсем оно.

В общем чтобы не мучиться я решил зашифровать кириллицу в url_encode. Потом делаешь url_decode — и кириллица работает. Но есть проблема — знаки тегов тоже конвертируются, и это мешает парсить HTML. Я решил проблему путём написания простенькой функции. Теперь код выглядит так:

'<',  '%3E' =>'>',  '%3D' =>'=',
                    '+' =>' ', '%22' =>'"', '%27' =>'\'',
                    '%21' =>'!','%2F' =>'/','%3A' =>':'
       );
       $str=strtr(urlencode($str),$tr);
       return $str;
    }

$dom = new domDocument('1.0', 'UTF-8');
$dom->loadHTML(my_urlencode("

Германия, Великобритания и Франция

")); // Тут работает foreach($dom->getElementsByTagName('p') as $p_dom) echo urldecode($p_dom->nodeValue); // И так работает echo urldecode($dom->saveHTML()); ?>

Теперь точно работает.

Как написать парсер HTML на PHP: 3 комментария

  1. virua

    Для работы в связке UTF-8 + HTML + DOMDocument можно также использовать функцию mb_convert_encoding.

    Например,

    $content = «Германия, Великобритания и Франция»;
    $content = mb_convert_encoding($content, ‘html-entities’, ‘utf-8′);

    $dom = new domDocument(’1.0′, ‘UTF-8′);
    $dom->loadHTML($content);

    // выводим на экран
    echo $dom->saveHTML();

    // результат на экране:
    // Германия, Великобритания и Франция

  2. jaguar

    virua пишет:

    Для работы в связке UTF-8 + HTML + DOMDocument можно также использовать функцию mb_convert_encoding.

    Очень спасибо :) А то у меня под одной виндой парсило сайт с кирилицей и без этого, а под другой никак не хотело.

  3. Vavan

    а у мну что-то не получается.. я делаю так: $dom->loadHTML(«ссылка на сай»); и не работает. или туда надо уже готовый вытянутый HTML код сайта? тогда как его вытянуть?

Комментарии запрещены.