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



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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Содаём объект
$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, но ситуация ещё сложнее. Вот пример

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$dom = new domDocument('1.0', 'UTF-8');
$dom->loadHTML("<p>Германия, Великобритания и Франция</p>");
 
// Тут работает
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. Я решил проблему путём написания простенькой функции. Теперь код выглядит так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
 
function my_urlencode($str)
    {
    // То же urlencode, но только кириллицы
       $tr = array( '%3C' =>'<',  '%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("<p>Германия, Великобритания и Франция</p>"));
 
// Тут работает
foreach($dom->getElementsByTagName('p') as $p_dom)
    echo urldecode($p_dom->nodeValue);
 
// И так работает
echo urldecode($dom->saveHTML());
 
?>

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

Посты по теме:

  1. Воюем с MSIE 6. Условные комментарии.

Категории Веб-программирование, Не по теме |
автор: altesack / Вторник, Апрель 26, 2011 / 3 комментов »

3 комментов

    Для работы в связке 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();

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

    virua пишет:

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

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

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