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));
}
Ползет верстка
Я парсером получаю данные с сайта поставщика, используя следующее регулярное выражение:
Код:
'#(<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>
<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>
<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 - и как побороть не могу сообразить. Как вариант - это парсить таблицу и распихивать значения по свойствам. Но это достаточно трудоемкий процесс. может есть более простое решение?
Код:
<?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));
}
}
}
}
?>
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));
}
}
}
}
?>
Вобщем окончательный вариант выглядит следующим образом.
Функция 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));
}
}
}
}
$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));
}
}
}
}
Вызывающая функция normalizeBody
Код:
Тут три вещи, на которые надо обратить внимание. Во-первых, этот кусок
Код:
libxml_use_internal_errors(true);
$dom->loadHTML('<?xml encoding="UTF-8">' . $html);
libxml_clear_errors();
$dom->loadHTML('<?xml encoding="UTF-8">' . $html);
libxml_clear_errors();
Во-вторых, опять же обязательно проверять существование объекта.
И в третьих. Если вы получаете предупреждение "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>';
}
}
{
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 еще раз спасибо.
Ох уж мне эти визуальные вородоредакторы.
Цитата: mike
Это на клиенте (JavaScript) или на стороне сервера (PHP и т.п.) ? Нельзя просто прибить все div и span.
Ох уж мне эти визуальные вородоредакторы.
Ох уж мне эти визуальные вородоредакторы.
ну это сторонние вещи. моя задача - обработать их.
Вариант предложенный mfender в приципе самый оптимальный. Проверю