ФорумПрограммированиеPHP для идиотов → Нужен парсер простого HTML

Нужен парсер простого HTML

  • kostyl

    Сообщения: 5210 Репутация: N Группа: Джедаи

    Spritz 21 декабря 2010 г. 1:46, спустя 11 минут 18 секунд

    только есть еще прикол - нужно учитвыать много херни браузеров, которые дописывают незакрытые теги…
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 21 декабря 2010 г. 1:53, спустя 7 минут 21 секунду

    Спасибо kostyl, автомат может и хороший, но не тот. Мне надо HTML в 100 строк.
    ιιlllιlllι унц-унц
  • kostyl

    Сообщения: 5210 Репутация: N Группа: Джедаи

    Spritz 21 декабря 2010 г. 2:23, спустя 29 минут 45 секунд

    artoodetoo, да можно сделать автомат и в несколько строчек… я привёл подобие пировского…
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 21 декабря 2010 г. 2:34, спустя 10 минут 40 секунд

    Вот сам набросал

    <?php

    error_reporting(-1);

    class MyHtmlTidy
    {
    const
    TAG = '<(?:"[^"]*"|\'[^\']*\'|[^\'">])*>',
    ATTR = '\w++\s*=\s*"[^"]++"|\w++\s*=\s*\'[^\']++\'|\w++\s*=\s*[^\s]++';

    private
    $_goodTags = array('b', 'i', 'u', 's', 'p', 'a', 'img', 'br', 'hr'),
    $_selfClose = array('img', 'br', 'hr'),
    $_goodAttrs = array(
    'a' => array('href', 'title'),
    'img' => array('src', 'alt')),
    $_nest = array();

    public
    $errors = array();

    public function preparse($html)
    {
    $this->_nest = array();
    $this->errors = array();
    $text = preg_replace_callback('/('.self::TAG.')/Uus', array($this, '_replace'), $html);
    if (!empty($this->_nest)) {
    $this->errors[] = 'Unclosed tags ' . implode(', ', $this->_nest);
    $text .= '</' . implode('></', array_reverse($this->_nest)) . '>';
    }
    return $text;
    }

    private function _replace($matches)
    {
    $tag = $matches[1];

    preg_match('/^<\/?(\w++)/', $tag, $m);
    $tagName = strtolower($m[1]);
    $isSelfClosed = $tag{strlen($tag) - 2} == '/';
    $attrs = trim(substr($tag, strlen($m[0]), ($isSelfClosed ? -2 : -1)));

    if (!in_array($tagName, $this->_goodTags)) {
    $this->errors[] = 'Tag ' . $tagName . ' is deprecated';
    return '';
    }

    // Closing tag
    if ($tag{1} == '/') {
    if (empty($this->_nest) || end($this->_nest) != $tagName) {
    $this->errors[] = 'Odd close tag ' . $tagName;
    return '<' . $tagName . '></' . $tagName . '>';
    }
    array_pop($this->_nest);
    return '</' . $tagName . '>';
    }

    // Open tag or self-closing tag
    $isSelfClosed = $isSelfClosed || in_array($tagName, $this->_selfClose);

    if (!$isSelfClosed) {
    $this->_nest[] = $tagName;
    }

    if (!isset($this->_goodAttrs[$tagName])) {
    // No attributes at all
    if (strlen($attrs)) {
    $this->errors[] = 'Tag ' . $tagName . ' cannot have attributes';
    }
    $attrs = '';
    } else {
    // Check every attribute
    preg_match_all('/'.self::ATTR.'/Uus', $attrs, $m);
    $attrs = $m[0];
    foreach ($attrs as $i => $attr) {
    $p = strpos($attr, '=');
    $attrName = strtolower(trim(substr($attr, 0, $p)));
    if (!in_array($attrName, $this->_goodAttrs[$tagName])) {
    $this->errors[] = 'Wrong ' . $tagName . ' attribute ' . $attrName;
    unset($attrs[$i]);
    } else {
    $attrs[$i] = $attrName . '=' . trim(substr($attr, $p + 1));
    }
    }
    $attrs = count($attrs) ? (' ' . implode(' ', $attrs)) : '';
    }

    return '<' . $tagName . $attrs . ($isSelfClosed ? '/>' : '>');
    }
    }

    $t = new MyHtmlTidy();

    $html = <<<HTML
    <p class='blabla'>dslkldsldslsd<br>
    kjksdjsdk<a href="http://thesite.name/path" target="_new" title="ololo" onclick="javascript:doit('xxx')">djdkjdk</a>
    <img src=0.gif alt='pysh-pysh'>
    ds;lsd;; <b>skjskjsk kjdkjdkd
    HTML;

    header('Content-type: text/plain');

    echo $html;
    echo "\n===========================\n";

    $preparsed = $t->preparse($html);
    if (!empty($t->errors)) {
    echo implode("\n", $t->errors);
    echo "\n===========================\n";
    }
    echo $preparsed;


    вывод:

    <p class='blabla'>dslkldsldslsd<br>
    kjksdjsdk<a href="http://thesite.name/path" target="_new" title="ololo" onclick="javascript:doit('xxx')">djdkjdk</a>
    <img src=0.gif alt='pysh-pysh'>
    ds;lsd;; <b>skjskjsk kjdkjdkd
    ===========================
    Tag p cannot have attributes
    Wrong a attribute target
    Wrong a attribute onclick
    Unclosed tags p, b
    ===========================
    <p>dslkldsldslsd<br/>
    kjksdjsdk<a href="http://thesite.name/path" title="ololo">djdkjdk</a>
    <img src=0.gif alt='pysh-pysh'/>
    ds;lsd;; <b>skjskjsk kjdkjdkd</b></p>


    Кому нелениво, поищите дыры, пожалуйста.
    Спустя 114 сек.
    Не знаю как мне с тем же preg_replace_callback ухитриться еще переводы строк в br превращать.
    Наверное preg_split надо.
    ιιlllιlllι унц-унц
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 21 декабря 2010 г. 3:44, спустя 1 час 10 минут 33 секунды

    суров. домой придет поищу :-)
    Сапожник без сапог
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 27 декабря 2010 г. 5:12, спустя 6 дней 1 час 27 минут

    Использовать автомат - оно, конечно, правильней регулярки. Но автомат нужно построить почти такой же, как в браузеры юзают.
    Напрашивается идея его и юзать.

    Загрузить код в DOM (он сам закрывает теги и отбросить прочие разночтения.)
    За тем пройтись по дереву рекурсивным обходчиком … для разрешенных тегов, забирать и тег и содержимое. Для остальных - только голый текст брать.
    И код будет небольшой, и 100% никакой жук не пролезет.

    Спустя 89 сек.
    Дайте редактировать свои посты … хрен ошибки исправишь!
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 27 декабря 2010 г. 7:15, спустя 2 часа 3 минуты 31 секунду

    а ты не ошибайся
    Сапожник без сапог

Пожалуйста, авторизуйтесь, чтобы написать комментарий!