Модераторы: Aliance, skyboy, MoLeX, ksnk

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> HTML парсер 
:(
    Опции темы
Diesel Draft
Дата 15.7.2007, 23:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Для решения задачи мне надо написать шаблонизатор. Но тут просто запутался. Я могу сам написать но надо просто натолкнуть меня. Я даже сейчас все хорошо опишу.


Короче есть шаблон в HTML.
Для парсання я выбрал xml_parse(). А чтоб он умел парсить я использую свои функции.

Код

    function __construct(){
      
          $this->xmlparser = xml_parser_create();
          
         xml_set_object($this->xmlparser, &$this); 
         xml_set_element_handler($this->xmlparser, "startTag", "endTag"); 
         xml_set_character_data_handler($this->xmlparser, "innerTag");
         
         xml_set_default_handler($this->xmlparser, 'dataTag');
         xml_set_processing_instruction_handler($this->xmlparser, 'piTag');
          
      }



После парсинга на выходе должно получиться дерево. Оно может складываться з 2х типов данных: тег и литерал.
Тегом считается любой HTML тек, содержащий активный атрибут.

Например

Код

  <a fw:df='df' id='23'>
    dfdf
    <b> fg </b>
    
    <b arts='df' />
    <!-- dfdf -->
  </a>
  <p> fgfdgd f</p>


Здесь тег "а"  а остальное литералы. В плане дерева все выглядит так:

root = {
  [0] => '     '; // текст до тега
  [1]=> {
    [name] => 'a' // название тега
    [htmlattributes] = {
      [id] => '23'
    }
    [fwattributes]  = {
      [df] => 'df' 
    }
    [children] = { // дочерние теги и литералы
      [0] => '
    dfdf
    <b> fg </b>
    
    <b arts='df' />
    <!-- dfdf -->
     '
    } 
  }
  [2] => '
  <p> fgfdgd f</p>'
}



Думаю пример ясен.


Вопрос: как это реализовать, а то я уже настолько в мыслях запутался что уже не могу.




--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Diesel Draft
Дата 16.7.2007, 00:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Я тут подумал, может пропарсить сначала все HTML теги и создать дерево с 3х элементов: BeginTag, InnerText, EndTag. А потом оптимизировать дерево. Парсер может себе позволить работать долго, все таки он парсит только 1 раз, а потом берет з кеша


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Diesel Draft
Дата 16.7.2007, 23:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Что никто парсеров не делал


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Diesel Draft
Дата 18.7.2007, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Ех, вижу не кому не интересно. 
Тогда хотя бы буду писать что уже сделал.  smile 

Я тут надумал алгоритм. Основная проблема в том что не знаю кому принадлежит закрывающий тег. Я знаю что эту задачу можно решить стеком. Но не могу собраться с мыслями.
И так, я почистил список чтоб выделить унарные теги (<tagname />). 
В идеале мне надо найти какие закрывающие теги закивают именно активные теги. Есть вариант построит дерево и по проходу сделать новый список, но уже все активные теги суммирую в один литерал.

Ну что скажете?


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Mushu
Дата 20.7.2007, 09:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 257
Регистрация: 20.7.2004

Репутация: нет
Всего: -5



Слишком накрученно.
Сразу возникает вопрос а что ты будешь делать с одинарными тегами..?
PM MAIL   Вверх
Diesel Draft
Дата 20.7.2007, 11:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Одинарные теги имеют те же свойства что и парные, но по дефолту не имеют детей. 

Я знаю как это заделать, но не могу собраться с мыслями. Всегда что то кажется не так

Это сообщение отредактировал(а) Diesel Draft - 20.7.2007, 11:12


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Golda
Дата 21.7.2007, 14:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 460
Регистрация: 26.3.2007
Где: Ариель, Израиль

Репутация: 5
Всего: 42



Хм, интересная задача оказалась. Вот что у меня получилось пока что:

Код

<?php

class SimpleParser
{
    // resources
    var $parser;
    
    // error related
    var $errorCode = 0;
    var $errorSstring;
    var $currentLine = 0;
    var $currentColumn = 0;
    
    // target array
    var $data = array();
    
    // stacks
    var $datas = array();
    var $notActiveStack = array();
    
     // active namespace
    var $activens = '';
    
    // start byte of not-active tag
    var $startByte;
    
    // xml to parse
    var $xml;
    
    function parse($xml)
    {
        $this->errorCode = 0;
        
        if (empty($this->activens)) {
            $this->errorCode = 1000000;
            $this->errorString = 'No active nasmespace specified';
            return;
        }
        
        $this->xml = $xml;
        
        $this->parser = xml_parser_create('UTF-8');
        xml_set_object($this->parser, $this);
        xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
        xml_set_element_handler($this->parser, 'tagOpen', 'tagClose');
        xml_set_character_data_handler($this->parser, 'cdata');
        if (!xml_parse($this->parser, $xml)) {
            $this->data = array();
            $this->errorCode = xml_get_error_code($this->parser);
            $this->errorString = xml_error_string($this->error_code);
            $this->currentLine = xml_get_current_line_number($this->parser);
            $this->currentColumn = xml_get_current_column_number($this->parser);;
        }
    
        xml_parser_free($this->parser);
        
        if (!($this->errorCode)) {
            $this->data = $this->data['children'][0]['children'];        
        }
    }

    function filter($attribute)
    {
        return (strpos($attribute, $this->activens . ':') === 0);
    }

    function getTagStartByte()
    {
        $start = xml_get_current_byte_index($this->parser);
        $old = -1;
        while (($new = strpos($this->xml, '<', $old+1)) < $start) {
            $old = $new;
        }
        $this->startByte = $old;
    }
    
    function tagOpen($parser, $tag, $attribs)
    {
        
        $active = empty($this->data); //  we don't want to ignore all inside root element
        $html_attributes = $attribs;
        $active_attributes = array();
        if (!empty($attribs)) {
            $active_attributes_names = array_filter(array_keys($attribs), array(&$this, 'filter'));
            if (!empty($active_attributes_names)) {
                $active = true;
                $active_attributes = array_intersect_key($attribs, array_flip($active_attributes_names));
                $html_attributes = array_diff_key($attribs, $active_attributes);
            }
        }
        if ($active) {
            $count = count($this->data['children']);
            $this->data['children'][$count] = array('name' => $tag, 'htmlattributes' => $html_attributes, 'fmattributes' => $active_attributes, 'children' => array());
            $this->datas[] =& $this->data;
            $this->data =& $this->data['children'][$count];
        } 
        if (!$active) {
            if (empty($this->notActiveStack)) {
                $this->getTagStartByte();
            }
            $this->notActiveStack[] = $tag;
        }
    }

    function cdata($parser, $cdata)
    {
        if (!empty($this->notActiveStack)) return;
        
        $this->data['children'][$this->cdataIndex()] .= $cdata;
    }

    function tagClose($parser, $tag)
    {
        if (!empty($this->notActiveStack)) { 
            array_pop($this->notActiveStack);            
            if (empty($this->notActiveStack)) {
                $end_byte = xml_get_current_byte_index($this->parser);
                $this->data['children'][$this->cdataIndex()] .= substr($this->xml, $this->startByte, $end_byte - $this->startByte + 1);
            }
        } else { // active tag handling
            $this->data =& $this->datas[count($this->datas)-1];
            array_pop($this->datas);
        }
        
    }
    
    function cdataIndex()
    {
        $count = count($this->data['children']);
        if ($count === 0 || is_array($this->data['children'][$count-1])) {
            return $count;
        } else {
            return $count - 1;
        }
    }
}

set_magic_quotes_runtime(0);
$xml_parser = new SimpleParser;
$xml_parser->activens = 'fw';
$xml = file_get_contents(dirname(__FILE__) . '/test.xml');
$xml_parser->parse($xml);
print_r($xml_parser->data);
?>


Если вкратце, то активные теги разбираются полностью встроенными средствами XML Parser, а неактивные - "ручками". Смотрим, на каком байте парсер при открытии и закрытии тега, и выбираем нужный substring. Для исключения путаницы в ситуациях с вложенными одноименными неактивными тегами приходится отдельно хранить стек неактивных тегов.

Правда, этот класс нельзя считать законченным. Я тут отождествила номер байта с номером символа строки, так что в неоднобайтных кодировках будут проблемы. Ну да это доделать несложно. Больше проблем в ситуации, когда активный тег вложен в неактивный. Это уже проблема не реализации, а постановки задачи. Подумайте, что Вы будете делать в таком случае.

P.S. Тестирована на expat, на libxml2 функция xml_get_current_byte_index() может вести себя и по-другому (на php.net пишут, что ее результат может варьироваться в зависимости от имплементации XML Parser). Но, думаю, идея ясна

Добавлено @ 14:13
Тестировала на XML:

Код

<root>
<a fw:df='df' id='23'>
    dfdf
    <b> fg <i>something</i></b>
    
    <b arts='df' />
    <!-- dfdf -->
  </a>
  <p> fgfdgd f</p>
</root>


Это сообщение отредактировал(а) Golda - 21.7.2007, 14:17


--------------------
"For every problem, there exists a simple and elegant solution which is absolutely wrong." -- J. Wagoner, U.C.B. Mathematics
PM MAIL   Вверх
Diesel Draft
Дата 21.7.2007, 21:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Весьма интересно, заслужен плюсик smile . Но есть один недостаток. Мне надо парсить HTML, а он не всегда закуривает теги.

Сейчас нет времени, у меня срочный проект, который я должен закончить за ночь  smile , а потом напишу что у меня з этого (твоего кода) получится


Вот что у меня сейчас есть:

Код

<?php

define('PARSER_ATTRIBUTE_PREIX', 'FW:');

  
  class Parser{
      
    private $xmlparser;
    
    public $root = array();
    
    function __construct(){
      
          $this->xmlparser = xml_parser_create();
          
         xml_set_object($this->xmlparser, &$this); 
         xml_set_element_handler($this->xmlparser, "startTag", "endTag"); 
         xml_set_character_data_handler($this->xmlparser, "innerTag");
         
         xml_set_default_handler($this->xmlparser, 'dataTag');
         xml_set_processing_instruction_handler($this->xmlparser, 'piTag');
          
      }
      
      /**
      * @return bool
      */
      protected function _isControl($tagname, $attributes){
         foreach($attributes as $key=>$value){ 
             if(strrpos($key, PARSER_ATTRIBUTE_PREIX) === 0){
                return true;
             }
         }
         
         return false;
      }
      
    
      
      protected function startTag($parser, $tagname, $attributes){

            $con = array();
            $con['type']        = 'T';
            $con['name']        = $tagname;
           // $con['children']    = array();
            $con['attributes']  = $attributes;
            $con['closed']      = false;

            $this->root[]       = $con;
           
            return $parser; 
      }
      
      protected function innerTag($parser, $characterData){
          
             $con = array();
             $con['type']        = 'L';
             $con['text']        = $characterData;
             
             $this->root[]       = $con; 
             
             
             return $parser;
      }
      
      protected function endTag($parser, $tagname){
          
             $con = array();
             $con['type']        = 'E';
             $con['name']        = $tagname;
             
             $this->root[]       = $con; 
            
            return $parser;
      }
          
      
      function dataTag($parser, $data){
          
             $con = array();
             $con['type']        = 'L';
             $con['text']        = $data;
             
             $this->root[]       = $con;           
          
           return $parser;
      }
      
      function piTag($parser, $target , $data){

        
           return $parser;
      }
      
      
      private function ArrayToTree($arr, $pos = 0){
      
      }
      
      
      public function parse($text){
          $parser =  xml_parse($this->xmlparser, $text, 1);
          
          // Оптимізація унарних тегів
          $count = count($this->root);
          for($j=0; $j<$count; $j++){
            if($this->root[$j]['type'] == 'E' && $this->root[$j-1]['type'] == 'T' && $this->root[$j-1]['closed'] == false && $this->root[$j-1]['name'] == $this->root[$j]['name']){
                $this->root[$j-1]['closed'] = true;
                array_splice($this->root, $j, 1);
            }
          }
          
          ksort($this->root);
          
          // Групування літералів
          
          $tree = $this->ArrayToTree($this->root, 0);
          
          return $this->root;
      }      
  
  
  }
  
  
  
  $parser = new Parser();
  
  
  $text = "<?xm version='1.0'?>
  <a fw:df='df' id='23'>
    dfdf
    <b> fg </b>
    
    <br class='df' />
    <!-- dfdf -->
  </a>
  
  
  ";
  
 //var_dump($parser->parse($text));
 
 
 foreach($parser->parse($text) as $item){
     if($item['type'] == 'L'){
        echo $item['text'];
     }elseif($item['type'] == 'E'){
        echo '</' . $item['name'] . '>';
     }elseif($item['type'] == 'T'){
        echo '<' . $item['name'];      
        
        foreach($item['attributes'] as $name=>$value){
            echo ' ' . $name . '="' . $value . '"';
        }
        
        if($item['closed']){
         echo '/';   
        }
        echo  '>';
     }
 }
?>




П.С у меня HTML тоже не хочет парсерит. У меня тажа проблема.  Главное я знаю как решить, но просто не могу сосредоточитса, поетому пишю сюда. ешение просто - надо узнать какие закрывающые теги актывны, и опредилить детей, а потом додать всех  не активных соседей

Это сообщение отредактировал(а) Diesel Draft - 21.7.2007, 21:35


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Golda
Дата 22.7.2007, 15:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 460
Регистрация: 26.3.2007
Где: Ариель, Израиль

Репутация: 5
Всего: 42



Diesel Draft, за + спасибо.

Хочу уточнить. Что Вы имеете в виду под незакрывающимися тегами?
Если случай <tag attr='val'/>, то на нем мой алгоритм работает хорошо. А если <tag attr='val'> без парного закрывающего, то задача принципиально не имеет решения. Для невалидного XML xml_parse()всегда вернет false

P.S. Если будете развивать свой пример, вынесите xml_set_object()  из конструктора. Об этом много информации в комментариях пользователей на http://us2.php.net/manual/en/function.xml-set-object.php



Это сообщение отредактировал(а) Golda - 22.7.2007, 15:28


--------------------
"For every problem, there exists a simple and elegant solution which is absolutely wrong." -- J. Wagoner, U.C.B. Mathematics
PM MAIL   Вверх
Diesel Draft
Дата 22.7.2007, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Спасибо. Задача решаетса доволе просто, но запутано (вот я и запутался). Короче алгоритм такой: бедуем дерево , оно нужно для того чтоб узнать какие теги кому принадлежит. Потом проходим по дереву и снов создаем список. Теперь мы можем пододавать все не активные теги, ясли конечно они стоят рядом. Потом снов создаем дерево и все  smile 

Это сообщение отредактировал(а) Diesel Draft - 22.7.2007, 18:20


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Diesel Draft
Дата 23.7.2007, 00:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Блн, мне просто надо пропарсить HTML и получить DOM или подобную модель

Тема актуальна


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Diesel Draft
Дата 23.7.2007, 01:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Я вот не понимаю, если есть ошибка то после не хочет парсер видеть закрывающие теги.
Например:
видит
Код

  <a fw:df='df' id='23'>
    dfdf
    <b> fg </b>
    
    <br class='df' />
    <!-- dfdf -->
  </a>

не видит
Код

  <a fw:df='df' id='23'>
    dfdf
    <b> fg </b>
    
    <br class='df' >
    <!-- dfdf -->
  </a>



Что мне прийдетса всетаки использувать XHTML?  smile 


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Diesel Draft
Дата 23.7.2007, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



Покопался в интернете и знайшов вот такой партер http://php-html.sourceforge.net/ .Думаю заюзать его 


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Diesel Draft
Дата 27.7.2007, 15:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 876
Регистрация: 18.1.2005
Где: Lviv, Ukraine

Репутация: нет
Всего: 5



тема актуальна


--------------------
НЕДОМА в маси 
PM MAIL WWW ICQ GTalk   Вверх
Golda
Дата 27.7.2007, 15:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 460
Регистрация: 26.3.2007
Где: Ариель, Израиль

Репутация: 5
Всего: 42



Diesel Draft, у Вас слишком специфические требования. HTML, который включает не HTML-аттрибуты. Парсеры, ориентированные на XML, Вам не подойдут, т.к. well-formness Вы не гарантируете. Можно использовать что-то, ориентированное на HTML, но могут возникнуть проблемы с использованием namespaces (судя по соседней теме, Вы с ними уже столкнулись). Все что приходит в голову, чтобы не писать парсер с нуля, возьмите какой-нибудь open-source HTML-парсер, например, тот, о котором Вы писали в одном из передыдущих постов, или выдерите из кода какого-нибудь open-source броузера, и переделывайте его под себя


--------------------
"For every problem, there exists a simple and elegant solution which is absolutely wrong." -- J. Wagoner, U.C.B. Mathematics
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: X технологии | Следующая тема »


 




[ Время генерации скрипта: 0.1744 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.