Xpoint
   [напомнить пароль]

Парсинг HTML тагов

Метки: [без меток]
2006-07-18 15:57:48 [обр] Андрей Анатольич+(0/46)[досье]

Как можно распарсить следующий таг

<INPUT type ="Text"   name=text value='Something  value'>

?

На выходе должно получиться:

array(
   'type' => 'text',
   'name' => 'text',
   'value' => 'Something  value'
)

Хотелось бы узнать алгоритм парсинга, либо готовое решение (спец. классы не предлагать) ).

спустя 1 час 2 минуты [обр] arto(2/497)[досье]
info bison
спустя 1 час 47 минут [обр] Michael Yevdokimov(0/3)[досье]
Сам не пробовал, но теоретически можно было бы посмотреть в сторону DOM (получаем узел, имена аттрибутов и вытаскиваем значения по имени аттрибута).
спустя 15 минут [обр] Алексей Севрюков(0/1292)[досье]
Michael Yevdokimov[досье] Мы в теории и в алгоритмах :-)
Андрей Анатольич[досье] На чем Вы собираетесь реализовывать этот алгоритм?
спустя 2 часа 43 минуты [обр] † Stratos †[досье]

Андрей Анатольич[досье], какая предполагается в использовании версия PHP? 4 или 5?

если использовать 5ую версию, тогда можно, как и сказал Michael Yevdokimov[досье], загнать html код в дерево DOM.

например, вот так

?php
$dom = new DomDocument(); 
$dom->loadHTMLFile("http://www.php.net/"); 
$title = $dom->getElementsByTagName("title"); 
echo $title->item(0)->textContent; 
?
спустя 4 часа 41 минуту [обр] Thomas(0/7)[досье]
Все намного проще - xml_parse_into_struct только предварительно закрыть все теги нужно тоесть привести в XHTML виду.
(PHP 3>= 3.0.8, PHP 4 ) и выше.
спустя 7 часов [обр] Иванов Михаил aka Ivanych(0/70)[досье]

Алексей Севрюков[досье]
Если мы в теории и алгоритмах, то какая разница, на чем он будет реализован:)

Пример алгоритма.

0. Если считать, что тэг уже найден, то:
1. Ищем в теге участки, состоящие из имени, знака равно и значения в кавычках
2. Выделяем из каждого участка имя и значение
3. Из выделенных пар имя-значение составляем хеш
спустя 1 час 49 минут [обр] Алексей Севрюков(0/1292)[досье]
Иванов Михаил aka Ivanych[досье] Разницы никакой, просто тут уже начали сыпать готовые решения с использованием модулей, я и хотел уточнить у автора наводящими вопросами а нужно ли ему именно готовое решение.
P.S. 1 пункт надо поправить. У автора name=text без кавычек :-)
спустя 34 минуты [обр] Андрей Анатольич+(0/46)[досье]

Алексей Севрюков[досье]
Да, хочется понять именно алгоритм, как парсить таги. Использовать REGEXP?

Сейчас как раз пробую написать регулярное выражение, выдирающее ключ=значение из тага.

спустя 1 минуту [обр] Андрей Анатольич+(0/46)[досье]
Забыл добавить — реализация алгоритма в php 4
спустя 7 минут [обр] Thirteensmay(0/157)[досье]
>> Хотелось бы узнать алгоритм парсинга, либо готовое решение (спец. классы не предлагать).
Андрей Анатольич[досье] Xочется понять именно алгоритм ? ;) Вот решение в лоб, дальше сами исправляйте как надо и оптимизируйте.
$tag = " <input type ='text'  name =  text id='text' size=50
\tstyle= 'margin:1; padding:1' disabled value= \"S\n\rom'e' = Text...\" >  \n";
print "------------------------------ TAG: -------------------------------\n";
print $tag;
print "-------------------------- PARSE RESULT: --------------------------\n";

# Преобразуем концы строк (\n\r) в символ конца страницы, с целью корректности
# дальнейшей работы рег. выражений, после парсинга вернем на место
$tag =~ s/[\n\r]/\f/g;
# Обрезаем лидирующие и хвостовые пробелы, вместе с маркерами тега (<>)
$tag =~ s/\s*<(.*\S)\s*>\s*/$1/;
# Определяем имя тега и вырезаем его, ибо забодает, впредь поступаем также
$tag =~ s/^(\S+)\s*(.*)/$2/; push(@res, $1);

# Парсим тег до упора
while (parse()) {}
# Имеем результирующий массив @res вида:
# tagname, attr1name, attr1value, attr2name, attr2value, и т.д...

# Возвращаем символы \n на место (заменяем ранее подставленные \f)
foreach (@res) { $_ =~ s/\f/\n/g; }

print join("\n", @res);
print "\n-------------------------------------------------------------------";

sub parse
{
  # Определяем имя атрибута и вырезаем его из тега
  $tag =~ s/^([^\s=]+)(.*)/$2/; push(@res, $1);

  # Определяем значение атрибута и вырезаем его из тега
  # Проверяем есть ли у него вообще значение, вдруг это атрибут типа disabled ?
  # Делаем вывод по факту наличия знака '='
  if ($tag =~ /^\s*=/)
  {
    # Значение есть, извлекаем его
    # Обрезаем лидирующие пробелы и знак '='
    $tag =~ s/^[\s=]*(.*)/$1/;

    # Определяем стратегию кавычек, если значение заключено в двойные то допускаем
    # внутри одинарные, и наоборот, если кавычек нет - считываем до первого пробела
    # Определяем наличие/тип кавычек
    $tag =~ /^(.)/;
    if ($1 eq '"') { $tag =~ s/^"([^"]*)"\s*(.*)/$2/; push(@res, $1); }
    elsif ($1 eq "'") { $tag =~ s/^'([^']*)'\s*(.*)/$2/; push(@res, $1); }
    else { $tag =~ s/^([^\s]*)\s*(.*)/$2/; push(@res, $1); }
  }
  else
  { # Значения у атрибута нет
    # Обрезаем лидирующие пробелы (перед именем следующего атрибута)
    $tag =~ s/^\s*(.*)/$1/;
    push(@res, '');
  }

return length($tag);
}
спустя 4 минуты [обр] Андрей Анатольич+(0/46)[досье]

Иванов Михаил aka Ivanych[досье]
Да, в теории кажется просто, на практике же попадаю в тупик. Непонимаю например, как распарсить такое:

<INPUT type ="Text"   name=text value == class=class id='identity'>

А браузеры это понимают.
Тут, каким-то образом нужно анализировать, заключено ли значение в кавычки. Если заключено, брать все, что идет от начальной до первой встречной кавычки.
Если значение не заключено в кавычки, берем все, до первого пробела (перед значением тоже могут быть пробелы).

Вот на этом у меня полный стопор, не знаю откуда начать.

спустя 8 минут [обр] Thirteensmay(0/157)[досье]
С предыдущего поста начинайте ;)
спустя 47 минут [обр] Андрей Анатольич+(0/46)[досье]

Может конечно тупо, но попробовал сделать следующим образом:

   $tag = '<input type = text name="author" id=\'identity\' value="something text">';
   
   // Выбираем ключи=значения, где значения заключены в двойные кавычки
   preg_match_all('/([^=\s]+\s*=\s*".+?")/', $tag, $m1);
   
   // Выбираем ключи=значения, где значения заключены в одинарные кавычки
   preg_match_all('/([^=\s]+\s*=\s*\'.+?\')/', $tag, $m2);
   
   print_r($m1[0]);
   print_r($m2[0]);

А как выбрать ключи=значения, где значения не заключены в кавычки?

спустя 4 минуты [обр] Иванов Михаил aka Ivanych(0/70)[досье]
<INPUT type ="Text" name=text value == class=class id='identity'>
Вы бы сначала с валидным XML разобрались. А для таких извращений общего решения не существует, нужно знать конкретные условия.
спустя 4 минуты [обр] Андрей Анатольич+(0/46)[досье]

Иванов Михаил aka Ivanych[досье]
Знаю, как писать валидно. Но мне нужно парсить именно такие извращения, ведь никогда не знаешь, как человек может написать. Поэтому я пытаюсь выловить то, что надо, даже если верстальщик будет допускать вышеозначенные ошибки.

Кстати, это решение Парсинг HTML тагов (340427)
прекрасно парсит этот код <INPUT type ="Text"  name=text value == class=class id='identity'>

спустя 22 минуты [обр] Андрей Анатольич+(0/46)[досье]
Кстати, вот решение (на быструю руку накидано):
   $tag = '<INPUT type ="Text"   name=text value == class=class id=\'identity\'>';
   
   // Выбираем ключи=значения, где значения заключены в двойные кавычки
   preg_match_all('/([^=\s]+\s*=\s*".+?")/', $tag, $m1);
   
   // Выбираем ключи=значения, где значения заключены в одинарные кавычки
   preg_match_all('/([^=\s]+\s*=\s*\'.+?\')/', $tag, $m2);
   
   // Выбираем ключи=значения, где значения без кавычек
   preg_match_all('/([^=\s]+\s*=\s*[^\s"\']+)/', $tag, $m3);
   
   
   $dirty_attribs = array();
   foreach ($m1[0] AS $item) $dirty_attribs[] = $item;
   foreach ($m2[0] AS $item) $dirty_attribs[] = $item;
   foreach ($m3[0] AS $item) $dirty_attribs[] = $item;
   
   $attribs = array();
   foreach ($dirty_attribs AS $item) {
      preg_match('/^([^=]+?)\s*=\s*(.+)$/', $item, $match);
      
      // Удаляем кавычки по бокам значения
      if (($match[2][0] == '"' && $match[2][strlen($match[2])-1] == '"') or 
          ($match[2][0] == '\'' && $match[2][strlen($match[2])-1] == '\'')) {
         $match[2][0] = ' ';
         $match[2][strlen($match[2])-1] = ' ';
      }
      
      $match[2] = trim($match[2]);
      $attribs[strtolower($match[1])] = $match[2];
   }
   
   echo '<pre>';
   print_r($attribs);
спустя 22 часа [обр] Denis Usenko(0/7)[досье]

Чтоб не создавать еще одну такую же тему, спрошу здесь же мнения бывалых (надеюсь хозяин темы не обидется).
Насколько корректен следующий код ? (заготовка парсера html):

{
    my $html;
    {
        local(*file);
        open(file,shift);
        read(file,$html,-s file);
        close(file);
    }

    my $arg = qr~(\w+)(?:\s* = \s*(?:"([^"]*)"|'([^"]*)'|([^\s>]*)))?\s*~x;
    my $arglist = qr~\s*((?:$arg|(?:.*?))*)~x;
    my $starttag = qr~<(\w+)$arglist>~x;
    my $endtag = qr~</(\w+)>~x;
    my $comment = qr~<!(?:(?:--.*?--)|(?:-.?-))>~x;
    my $invalid = qr~[<>]~x;
    my $text = qr~[^<>]+~x;

    my $stoptags = qr(script|style)x;

    my @tags;

    while(){

        if($html =~ /\G$starttag/gcis){

            my $tagname = lc($1);
            my $args = $2;
            my $arghash = {};

            while($args =~ /$arg/gis){
                $arghash->{lc($1)} = $2|$3|$4;
            }

            push @tags,
                {
                    tagname => $tagname,
                    arglist => $arghash
                };

            if($tagname=~/^$stoptags$/){
                my $stop=$&;
                $html=~/.*?(?=<\/$stop>)/gcis;
            };

            next
        }

        if($html =~ /\G$endtag/gcis){
            next
        }

        if($html =~ /\G((?:$comment)|(?:$invalid)|(?:$text))/gcis){
            next
        }

        if($html =~ /\G$/gs){
            last
        }
    }

    # Проверочный вывод

    for my $item (@tags){

        print qq(\n<$item->{tagname});
        for my $argname (sort keys %{$item->{'arglist'}}){

            my $argvalue = %{$item->{'arglist'}}->{$argname};
            print ' ' . $argname . ( defined $argvalue ? qq( = "$argvalue") : "" );
        }
        print ">";

    }
}

Пока он только собирает теги в массив @tags. Если концептуальных оплошностей нет, то буду развивать именно этот вариант.

Powered by POEM™ Engine Copyright © 2002-2005