Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Ползет верстка

1
18 мая 2013 года
kot_
7.3K / / 20.01.2000
В общем суть вопроса в следующем.
Я парсером получаю данные с сайта поставщика, используя следующее регулярное выражение:

 
Код:
'#(<div align=justify>.*?</div>)#ims'
задача которого - получить описание данного товара. В результате я получаю примерно вот такой код:

Код:
<div align=justify><TABLE border=0 cellSpacing=0 cellPadding=0 width=384 x:str>
<TBODY>
<TR height=17>
<TD height=17 width=133><STRONG>Тип</STRONG></TD>
<TD width=251>Проводная оптическая мышь</TD></TR>
<TR height=17>
<TD height=17><STRONG>Цвет</STRONG></TD>
<TD>Black</TD></TR>
<TR height=17>
<TD height=17><STRONG>Интерфейс</STRONG></TD>
<TD>USB</TD></TR>
<TR height=17>
<TD height=17><STRONG>Разрешение</STRONG></TD>
<TD>3200 dpi</TD></TR>
<TR height=17>
<TD height=17><STRONG>Кнопки</STRONG></TD>
<TD>4 шт. + 1 колесо-кнопка</TD></TR>
<TR height=17>
<TD height=17><STRONG>Скролл</STRONG></TD>
<TD>В двух направлениях</TD></TR>
<TR height=17>
<TD height=17><STRONG>Поддержка ОС</STRONG></TD>
<TD>Windows 98/2000/ME/NT/XP и MAC 8.6</TD></TR>
<TR height=17>
<TD height=17><STRONG>Дополнительно</STRONG></TD>
<TD>
<P>Эргономическая форма SteelSeries Kinzu v2 обеспечивает комфортное использование любым из трех типов хвата мышки - коготь, пальцы либо ладонь - и легкий доступ ко всем четырем кнопкам, независимо от того, играет ли пользователь в молниеносные игры жанра FPS или затяжные игры, как MMO.</P></TD></TR>
<TR height=17>
<TD height=17><STRONG>Размеры</STRONG></TD>
<TD>117 x 64 x 36 mm</TD></TR>
<TR height=17>
<TD height=17><STRONG>Сайт производителя</STRONG></TD>
<TD><U><A href="http://www.steelseries.com/">www.steelseries.com</A></U></TD></TR></TBODY></TABLE></div>
Здесь все нормально и нет никаких проблем.
Но в ряде случаев я получаю вот такой результат:

Код:
<div align=justify><TABLE style="WIDTH: 380px; HEIGHT: 819px" id=PriceTable border=0 cellSpacing=3 cellPadding=0>
<TBODY>
<TR>
<TD width="33%"><STRONG>Код модели</STRONG></TD>
<TD width="33%" colSpan=3>
<P><SPAN style="WIDOWS: 2; TEXT-TRANSFORM: none; BACKGROUND-COLOR: rgb(255,255,255); TEXT-INDENT: 0px; FONT: 12px Arial, Verdana, Helvetica, sans-serif; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: rgb(0,0,0); WORD-SPACING: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px" class=Apple-style-span><SPAN style="WIDOWS: 2; TEXT-TRANSFORM: none; BACKGROUND-COLOR: rgb(240,240,240); TEXT-INDENT: 0px; DISPLAY: inline !important; FONT: 12px Arial, Verdana, Helvetica, sans-serif; WHITE-SPACE: normal; ORPHANS: 2; FLOAT: none; LETTER-SPACING: normal; COLOR: rgb(0,0,0); WORD-SPACING: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">C4Y85EA</SPAN></SPAN></P></TD></TR>
<TR>
<TD width="33%"><STRONG>Описание</STRONG></TD>
<TD width="67%" colSpan=3><SPAN style="FONT-FAMILY: Arial; FONT-SIZE: 10pt">Ноутбук на базе технологии Intel i3</SPAN></TD></TR>
<TR>
<TD width="33%"><STRONG>Процессор</STRONG></TD>
<TD width="67%" colSpan=3><FONT style="BACKGROUND-COLOR: #ffffff" color=#000000><SPAN style="WIDOWS: 2; TEXT-TRANSFORM: none; BACKGROUND-COLOR: rgb(231,231,231); TEXT-INDENT: 0px; FONT: 12px Arial, Verdana, Helvetica, sans-serif; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: rgb(0,0,0); WORD-SPACING: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px" class=Apple-style-span>I<FONT style="BACKGROUND-COLOR: #ffffff">ntel Core </FONT><FONT style="BACKGROUND-COLOR: #ffffff">i3-3110M (2,4 ГГц)</FONT></SPAN></FONT></TD></TR>
<TR>
<TD width="33%"><STRONG>Кэш L3</STRONG></TD>
<TD width="67%" colSpan=3>3 Mb</TD></TR>
<TR>
<TD width="33%"><STRONG>Чипсет</STRONG></TD>
<TD width="67%" colSpan=3>
<DIV>
<DIV>
<DIV>
<DIV>
<DIV>
<DIV>
<DIV>
<DIV>
<DIV align=left>
<DIV>
<DIV><SPAN style="WIDOWS: 2; TEXT-TRANSFORM: none; BACKGROUND-COLOR: rgb(231,231,231); TEXT-INDENT: 0px; FONT: 12px Arial, Verdana, Helvetica, sans-serif; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: rgb(0,0,0); WORD-SPACING: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px" class=Apple-style-span><FONT style="BACKGROUND-COLOR: #ffffff">Intel HM76</FONT></SPAN></DIV></td></tr><table></div>
в котором, как видно, присутсвуют лишние элементы DIV и в результате ползет верстка страницы. Пример - http://www.shop.varkon.biz/products/64847-hp-probook-4540s-i3-3110-4g-500-drw-hd7650-wf-156hd-lnx-bag.html
Не могу сообразить, как автоматизировать процесс исправления - мне достаточно что бы таблица осталась, а лишние элементы оттуда убрать - или экранировать каким то образом что бы верстка не ползла. На сайте поставщика этой проблемы нет - у них табличная верстка. У меня верстка в DIV - и как побороть не могу сообразить. Как вариант - это парсить таблицу и распихивать значения по свойствам. Но это достаточно трудоемкий процесс. может есть более простое решение?
8
18 мая 2013 года
mfender
3.5K / / 15.06.2005
На скорую руку такой вариант набросал. Можно понавороченней сделать, но для этого частного случая думаю покатит.

Код:
<?php
error_reporting(0);

// В файле текст второго кода из СТ.
$HTML = file_get_contents("dom.html");

$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="UTF-8">' . $HTML);
$dom->normalizeDocument();

/**  Узлы по именам тэгов, которые желаем грохнуть */
$tags = array("div", "strong", "p", "span", "font");

/**  Аттрибуты узлов, которые нужно убрать */
$attributes = array("width", "style", "id", "border", "cellspacing", "cellpadding");

$topDiv = $dom->getElementsByTagName("div")->item(0);
$topDiv->normalize();
CleanCode($dom, $topDiv, $tags, $attributes);

file_put_contents("dom1.html", $dom->saveHTML($topDiv->childNodes->item(0)));


/**
 * Рекурсивная чистка HTML-кода от ненужных тэгов и аттрибутов
 * @param DOMDocument $doc
 * @param DOMElement $el
 * @param array $tags
 * @param array $attributes
 * @param boolean $keepTextNode - Флаг, сообщающий стоит ли текст из удаляемых
 * узлов перемещать в родительский узел.
 */

function CleanCode(&$doc, &$el, $tags = [], $attributes = [], $keepTextNode = true) {
    $children = $el->childNodes;
    for ($i = 0; $i < $children->length; $i++) {
        if ($children->item($i)->nodeType == XML_ELEMENT_NODE) {
            $do = $children->item($i);

            if ($do->hasAttributes()) {
                foreach ($attributes as $attr) {
                    if ($do->hasAttribute($attr)) {
                        $do->removeAttribute($attr);
                    }
                }
            }

            CleanCode($doc, $do, $tags, $attributes, $keepTextNode);

            if (in_array($children->item($i)->nodeName, $tags)) {
                $parent = $children->item($i)->parentNode;
                if ($keepTextNode) {
                    $innerHTML = $children->item($i)->childNodes->item(0);
                    $parent->appendChild($innerHTML);
                }
                $parent->removeChild($children->item($i));
            }
        }
    }
}
?>
1
28 мая 2013 года
kot_
7.3K / / 20.01.2000
Совсем замотался.
Вобщем окончательный вариант выглядит следующим образом.
Функция CleanCode

Код:
static function CleanCode(&$doc, &$el, $tags = array(), $attributes = array(), $keepTextNode = true) {
            $children = $el->childNodes;
            for ($i = 0; $i < $children->length; $i++) {
                if ($children->item($i)->nodeType == XML_ELEMENT_NODE) {
                    $do = $children->item($i);

                    if ($do->hasAttributes()) {
                        foreach ($attributes as $attr) {
                            if ($do->hasAttribute($attr)) {
                            $do->removeAttribute($attr);
                            }
                        }
                    }

            self::CleanCode($doc, $do, $tags, $attributes, $keepTextNode);

            if (in_array($children->item($i)->nodeName, $tags)) {
                $parent = $children->item($i)->parentNode;
     
                    if ($keepTextNode) {
                        $innerHTML = $children->item($i)->childNodes->item(0);
                      if(isset($innerHTML))// Не забываем проверять  - существует ли
                        $parent->appendChild($innerHTML);
     
                    }
                    $parent->removeChild($children->item($i));
                }
            }
        }
    }
Здесь важно проверять - существует ли нужный нам элемент - прежде чем передавать его в appendChild
Вызывающая функция normalizeBody

Код:
static function normalizeBody($html)
        {
            $dom = new DOMDocument();
            libxml_use_internal_errors(true);
            $dom->loadHTML('<?xml encoding="UTF-8">' . $html);
            libxml_clear_errors();
            $dom->normalizeDocument();

/**  Узлы по именам тэгов, которые желаем грохнуть */
            $tags = array("div", "strong", "p", "span", "font");

/**  Аттрибуты узлов, которые нужно убрать */
            $attributes = array("width", "style", "id", "border", "cellspacing", "cellpadding");

            $topDiv = $dom->getElementsByTagName("div")->item(0);
            if(!isset($topDiv))return;
            $topDiv->normalize();
            self::CleanCode($dom, $topDiv, $tags, $attributes);
            return $dom->saveHTML($topDiv->childNodes->item(0));

}
на вход получает код, который нужно обработать, на выходе - возвращает обработанный.
Тут три вещи, на которые надо обратить внимание. Во-первых, этот кусок

 
Код:
libxml_use_internal_errors(true);
            $dom->loadHTML('<?xml encoding="UTF-8">' . $html);
libxml_clear_errors();
позволяет избавиться от ошибки в случае, если код HTML, которые мы обрабатываем невалиден (незакрытые теги и пр.). Если вызов не сделать - работа скрипта будет прервана на первой ошибке. Ну или подавить вывод ошибок стандартным образом, при помощи @
Во-вторых, опять же обязательно проверять существование объекта.
И в третьих. Если вы получаете предупреждение "Warning: DOMDocument::saveHTML() expects exactly 0 parameters, 1 given in..." то это значит баг Я просто обновил версию PHP.
В результате все работает весьма шустро. Пример использования:

Код:
public function actionCheckBody()
         {
              set_time_limit (10000);//Так как объектов может быть весьма много,
//но если не нужен вывод - то можно и обойтись, сам скрипт работает быстро
              $products = Product::model()->findAll();
              foreach ($products as $product){
                  $body = self::normalizeBody ($product->productbody);
                  $product->productbody = $body;
                  $product->save(false);
                  echo '<p>Product code '.$product->productid.' with name '.$product->productname.' was done</p>';
              }  
         }
можно в принципе обойтись и без промежуточной переменной.
камраду mfender еще раз спасибо.
4
18 мая 2013 года
mike
3.7K / / 01.10.2002
Это на клиенте (JavaScript) или на стороне сервера (PHP и т.п.) ? Нельзя просто прибить все div и span.

Ох уж мне эти визуальные вородоредакторы.
1
18 мая 2013 года
kot_
7.3K / / 20.01.2000
Цитата: mike
Это на клиенте (JavaScript) или на стороне сервера (PHP и т.п.) ? Нельзя просто прибить все div и span.

Ох уж мне эти визуальные вородоредакторы.


ну это сторонние вещи. моя задача - обработать их.
Вариант предложенный mfender в приципе самый оптимальный. Проверю

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог