Очистка HTML путем удаления лишних / избыточных тегов форматирования

Я использовал редактор CKEditor wysiwyg для веб-сайта, где пользователям разрешено использовать HTML-редактор для добавления комментариев. В итоге у меня был очень избыточный вложенный HTML-код в моей базе данных, который замедляет просмотр / редактирование этих комментариев.

У меня есть комментарии, которые выглядят так (это очень маленький пример. У меня есть комментарии с более чем 100 вложенными тегами):

<p> <strong> <span style="font-size: 14px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400">This is a </span> </span> </span> </span> </span> </span> </span> <span style="color: #006400"> <span style="font-size: 16px"> <span style="color: #b22222">Test</span> </span> </span> </span> </span> </strong> </p> 

Мои вопросы:

  • Есть ли библиотека / код / ​​программное обеспечение, способное очищать HTML-код (то есть формат), удаляя все избыточные теги, которые не влияют на форматирование (потому что они переопределены внутренними тегами)? Я пробовал много существующих онлайн-решений (таких как HTML Tidy ). Никто из них не делает то, что я хочу.

  • Если нет, мне нужно написать код для разбора и очистки HTML. Я планирую использовать PHP Simple HTML DOM для перемещения по дереву HTML и поиска всех тегов, которые не имеют никакого эффекта. Вы предлагаете любой другой парсер HTML, который более подходит для моей цели?

благодаря

,

Обновить:

Я написал код для анализа кода HTML, который у меня есть. Все теги HTML, которые у меня есть:

  • <span> со стилями для font-size и / или color
  • <font> с color и / или size атрибутов
  • <a> для ссылок (с href )
  • <strong>
  • <p> (одиночный тег, чтобы обернуть весь комментарий)
  • <u>

Я могу легко написать код для преобразования HTML-кода в bbcode (например, [b] , [color=blue] , [size=3] и т. Д.). Поэтому я над HTML станет чем-то вроде:

 [b][size=14][color=#006400][size=14][size=16][color=#006400] [size=14][size=16][color=#006400]This is a [/color][/size] [/size][/color][/size][/size][color=#006400][size=16] [color=#b22222]Test[/color][/size][/color][/color][/size][/b] 

Вопрос в следующем: есть ли простой способ (алгоритм / библиотека / etc) очистить грязный (как грязный, как этот оригинальный HTML) bbcode, который будет сгенерирован?

еще раз спасибо

Введение

Лучшее решение, которое вы видели до сих пор, – это использование HTML Tidy http://tidy.sourceforge.net/

Помимо преобразования формата документа, Tidy также может автоматически конвертировать устаревшие теги HTML в свои каскадные таблицы стилей (CSS) с помощью использования чистой опции. Сгенерированный вывод содержит встроенную декларацию стиля.

Он также гарантирует, что HTML-документ совместим с xhtml

пример

 $code ='<p> <strong> <span style="font-size: 14px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400">This is a </span> </span> </span> </span> </span> </span> </span> <span style="color: #006400"> <span style="font-size: 16px"> <span style="color: #b22222">Test</span> </span> </span> </span> </span> </strong> </p>'; 

Если вы RUN

 $clean = cleaning($code); print($clean['body']); 

Вывод

 <p> <strong> <span class="c3"> <span class="c1">This is a</span> <span class="c2">Test</span> </span> </strong> </p> 

Вы можете получить CSS

 $clean = cleaning($code); print($clean['style']); 

Вывод

 <style type="text/css"> span.c3 { font-size: 14px } span.c2 { color: #006400; font-size: 16px } span.c1 { color: #006400; font-size: 14px } </style> 

Наш ПОЛНЫЙ HTML

 $clean = cleaning($code); print($clean['full']); 

Вывод

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <style type="text/css"> /*<![CDATA[*/ span.c3 {font-size: 14px} span.c2 {color: #006400; font-size: 16px} span.c1 {color: #006400; font-size: 14px} /*]]>*/ </style> </head> <body> <p> <strong><span class="c3"><span class="c1">This is a</span> <span class="c2">Test</span></span></strong> </p> </body> </html> 

Используемая функция

 function cleaning($string, $tidyConfig = null) { $out = array (); $config = array ( 'indent' => true, 'show-body-only' => false, 'clean' => true, 'output-xhtml' => true, 'preserve-entities' => true ); if ($tidyConfig == null) { $tidyConfig = &$config; } $tidy = new tidy (); $out ['full'] = $tidy->repairString ( $string, $tidyConfig, 'UTF8' ); unset ( $tidy ); unset ( $tidyConfig ); $out ['body'] = preg_replace ( "/.*<body[^>]*>|<\/body>.*/si", "", $out ['full'] ); $out ['style'] = '<style type="text/css">' . preg_replace ( "/.*<style[^>]*>|<\/style>.*/si", "", $out ['full'] ) . '</style>'; return ($out); } 

================================================

Изменить 1: Грязный взлом (не рекомендуется)

================================================

На основе вашего последнего комментария это похоже на то, что вы хотите сохранить стильный стиль. HTML Tidy может не позволить вам сделать это с момента его depreciated но вы можете сделать это

 $out = cleaning ( $code ); $getStyle = new css2string (); $getStyle->parseStr ( $out ['style'] ); $body = $out ['body']; $search = array (); $replace = array (); foreach ( $getStyle->css as $key => $value ) { list ( $selector, $name ) = explode ( ".", $key ); $search [] = "<$selector class=\"$name\">"; $style = array (); foreach ( $value as $type => $att ) { $style [] = "$type:$att"; } $replace [] = "<$selector style=\"" . implode ( ";", $style ) . ";\">"; } 

Вывод

 <p> <strong> <span style="font-size:14px;"> <span style="color:#006400;font-size:14px;">This is a</span> <span style="color:#006400;font-size:16px;">Test</span> </span> </strong> </p> 

Используемый класс

 //Credit : http://stackoverflow.com/a/8511837/1226894 class css2string { var $css; function parseStr($string) { preg_match_all ( '/(?ims)([a-z0-9, \s\.\:#_\-@]+)\{([^\}]*)\}/', $string, $arr ); $this->css = array (); foreach ( $arr [0] as $i => $x ) { $selector = trim ( $arr [1] [$i] ); $rules = explode ( ';', trim ( $arr [2] [$i] ) ); $this->css [$selector] = array (); foreach ( $rules as $strRule ) { if (! empty ( $strRule )) { $rule = explode ( ":", $strRule ); $this->css [$selector] [trim ( $rule [0] )] = trim ( $rule [1] ); } } } } function arrayImplode($glue, $separator, $array) { if (! is_array ( $array )) return $array; $styleString = array (); foreach ( $array as $key => $val ) { if (is_array ( $val )) $val = implode ( ',', $val ); $styleString [] = "{$key}{$glue}{$val}"; } return implode ( $separator, $styleString ); } function getSelector($selectorName) { return $this->arrayImplode ( ":", ";", $this->css [$selectorName] ); } } 

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

Это также довольно тяжело, поэтому вы хотите сохранить вывод этой базы данных (в отличие от чтения raw из базы данных и затем разбора его очистителем каждый раз.

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

Вот пример: http://jsfiddle.net/mmeah/fUpe8/3/

 var fixedCode = readNestProp($("#redo")); $("#simp").html( fixedCode ); function readNestProp(el){ var output = ""; $(el).children().each( function(){ if($(this).children().length==0){ var _that=this; var _cssAttributeNames = ["font-size","color"]; var _tag = $(_that).prop("nodeName").toLowerCase(); var _text = $(_that).text(); var _style = ""; $.each(_cssAttributeNames, function(_index,_value){ var css_value = $(_that).css(_value); if(typeof css_value!= "undefined"){ _style += _value + ":"; _style += css_value + ";"; } }); output += "<"+_tag+" style='"+_style+"'>"+_text+"</"+_tag+">"; }else if( $(this).prop("nodeName").toLowerCase() != $(this).find(">:first-child").prop("nodeName").toLowerCase() ){ var _tag = $(this).prop("nodeName").toLowerCase(); output += "<"+_tag+">" + readNestProp(this) + "</"+_tag+">"; }else{ output += readNestProp(this); }; }); return output; } 

Лучшее решение для ввода всех возможных атрибутов css, таких как:
var _cssAttributeNames = ["font-size", "color"];
Является ли использование решения, упомянутого здесь: Может ли jQuery получить все стили CSS, связанные с элементом?

У меня нет времени, чтобы закончить это … может быть, кто-то может помочь. Этот javascript также удаляет точные повторяющиеся теги и запрещенные теги …

Есть несколько проблем / вещей, которые нужно сделать,
1) регенерированные теги должны быть закрыты
2) он удалит только тег, если имя тега и атрибуты идентичны другому в пределах этих узлов, поэтому его недостаточно «умным», чтобы удалить все ненужные теги.
3) он просмотрит разрешенные переменные CSS и извлечет ВСЕ эти значения из элемента, а затем напишет его на выходной HTML, например:

 var allowed_css = ["color","font-size"]; <span style="font-size: 12px"><span style="color: #123123"> 

Будет переведено на:

 <span style="color:#000000;font-size:12px;"> <!-- inherited colour from parent --> <span style="color:#123123;font-size:12px;"> <!-- inherited font-size from parent --> 

Код:

 <html> <head> <script type="text/javascript"> var allowed_css = ["font-size", "color"]; var allowed_tags = ["p","strong","span","br","b"]; function initialise() { var comment = document.getElementById("comment"); var commentHTML = document.getElementById("commentHTML"); var output = document.getElementById("output"); var outputHTML = document.getElementById("outputHTML"); print(commentHTML, comment.innerHTML, false); var out = getNodes(comment); print(output, out, true); print(outputHTML, out, false); } function print(out, stringCode, allowHTML) { out.innerHTML = allowHTML? stringCode : getHTMLCode(stringCode); } function getHTMLCode(stringCode) { return "<code>"+((stringCode).replace(/</g,"&lt;")).replace(/>/g,"&gt;")+"</code>"; } function getNodes(elem) { var output = ""; var nodesArr = new Array(elem.childNodes.length); for (var i=0; i<nodesArr.length; i++) { nodesArr[i] = new Array(); nodesArr[i].push(elem.childNodes[i]); getChildNodes(elem.childNodes[i], nodesArr[i]); nodesArr[i] = removeDuplicates(nodesArr[i]); output += nodesArr[i].join(""); } return output; } function removeDuplicates(arrayName) { var newArray = new Array(); label: for (var i=0; i<arrayName.length; i++) { for (var j=0; j<newArray.length; j++) { if(newArray[j]==arrayName[i]) continue label; } newArray[newArray.length] = arrayName[i]; } return newArray; } function getChildNodes(elemParent, nodesArr) { var children = elemParent.childNodes; for (var i=0; i<children.length; i++) { nodesArr.push(children[i]); if (children[i].hasChildNodes()) getChildNodes(children[i], nodesArr); } return cleanHTML(nodesArr); } function cleanHTML(arr) { for (var i=0; i<arr.length; i++) { var elem = arr[i]; if (elem.nodeType == 1) { if (tagNotAllowed(elem.nodeName)) { arr.splice(i,1); i--; continue; } elem = "<"+elem.nodeName+ getAttributes(elem) +">"; } else if (elem.nodeType == 3) { elem = elem.nodeValue; } arr[i] = elem; } return arr; } function tagNotAllowed(tagName) { var allowed = " "+allowed_tags.join(" ").toUpperCase()+" "; if (allowed.search(" "+tagName.toUpperCase()+" ") == -1) return true; else return false; } function getAttributes(elem) { var attributes = ""; for (var i=0; i<elem.attributes.length; i++) { var attrib = elem.attributes[i]; if (attrib.specified == true) { if (attrib.name == "style") { attributes += " style=\""+getCSS(elem)+"\""; } else { attributes += " "+attrib.name+"=\""+attrib.value+"\""; } } } return attributes } function getCSS(elem) { var style=""; if (elem.currentStyle) { for (var i=0; i<allowed_css.length; i++) { var styleProp = allowed_css[i]; style += styleProp+":"+elem.currentStyle[styleProp]+";"; } } else if (window.getComputedStyle) { for (var i=0; i<allowed_css.length; i++) { var styleProp = allowed_css[i]; style += styleProp+":"+document.defaultView.getComputedStyle(elem,null).getPropertyValue(styleProp)+";"; } } return style; } </script> </head> <body onload="initialise()"> <div style="float: left; width: 300px;"> <h2>Input</h2> <div id="comment"> <p> <strong> <span style="font-size: 14px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400">This is a </span> </span> </span> </span> </span> </span> </span> <span style="color: #006400"> <span style="font-size: 16px"> <span style="color: #b22222"><b>Test</b></span> </span> </span> </span> </span> </strong> </p> <p>Second paragraph. <span style="color: #006400">This is a span</span></p> </div> <h3>HTML code:</h3> <div id="commentHTML"> </div> </div> <div style="float: left; width: 300px;"> <h2>Output</h2> <div id="output"> </div> <h3>HTML code:</h3> <div id="outputHTML"> </div> </div> <div style="float: left; width: 300px;"> <h2>Tasks</h2> <big> <ul> <li>Close Tags</li> <li>Ignore inherited CSS style in method getCSS(elem)</li> <li>Test with different input HTML</li> </ul> </big> </div> </body> </html> 

Это может не точно решить вашу конкретную проблему, но то, что я бы сделал на вашем месте, – это просто полностью исключить все теги HTML, сохранить только текст боли и разрывы строк.

После этого переключитесь на уценку нашего bbcode, чтобы лучше форматировать ваши комментарии. WYSIWYG редко бывает полезным.

Причина состоит в том, что вы сказали, что все, что у вас было в комментариях, – это презентационные данные, которые, откровенно говоря, не так важны.

Очистка HTML сворачивает теги, которые, как представляется, вы запрашиваете. Однако он создает проверенный HTML-документ с CSS, перемещенным в встроенные стили. Многие другие форматирования HTML не будут делать этого, поскольку он изменяет структуру документа HTML.

Я помню, что Adobe (Macromedia) Dreamweaver, по крайней мере, несколько старых версий имела возможность «Очистить HTML», а также «Очистить слово html» для удаления избыточных тегов и т. Д. С любой веб-страницы.

Я знаю, что вы ищете очиститель HTML DOM, но, может быть, js может помочь?

 function getSpans(){ var spans=document.getElementsByTagName('span') for (var i=0;i<spans.length;i++){ spans[i].removeNode(true); if(i == spans.length) { //add the styling you want here } } } 

Вместо того, чтобы тратить драгоценное время на анализ сервера, плохой HTML, я бы предложил вам исправить корень проблемы.

Простым решением было бы ограничить символы, которые каждый комментатор мог бы включить, чтобы включить весь отсчет char html, а не только количество слов (по крайней мере, это остановило бы бесконечно большие вложенные теги).

Вы можете улучшить это, разрешив пользователю переключаться между HTML-представлением и текстовым представлением. Я уверен, что большинство людей увидели бы загрузку нежелательной информации в HTML-виде и просто CTRL + A & DEL it.

Я думаю, было бы лучше, если бы у вас были свои собственные символы форматирования, которые вы проанализировали бы и заменили форматированием, т. Е. Как переполнение стека имеет **bold text** , видимый плакату. Или просто BB-код сделал бы, видимо, на плакат.

Не пытайтесь анализировать HTML с помощью DOM, но, возможно, с SAX (http://www.brainbell.com/tutorials/php/Parsing_XML_With_SAX.htm)

SAX анализирует документ с самого начала и отправляет такие события, как «начало элемента» и «конец» элемента, для вызова функций обратного вызова, которые вы определяете

Затем вы можете создать своего рода стек для всех событий. Если у вас есть текст, вы можете сохранить эффект своего стека в этом тексте.

После этого вы обрабатываете стек для создания нового HTML только с нужным эффектом.

Если вы хотите использовать jQuery, попробуйте следующее:

 <p> <strong> <span style="font-size: 14px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400"> <span style="font-size: 14px"> <span style="font-size: 16px"> <span style="color: #006400">This is a </span> </span> </span> </span> </span> </span> </span> <span style="color: #006400"> <span style="font-size: 16px"> <span style="color: #b22222">Test</span> </span> </span> </span> </span> </strong> </p> <br><br> <div id="out"></div> <!-- Just to print it out --> $("span").each(function(i){ var ntext = $(this).text(); ntext = $.trim(ntext.replace(/(\r\n|\n|\r)/gm," ")); if(i==0){ $("#out").text(ntext); } }); 

В результате вы получаете это:

 <div id="out">This is a Test</div> 

Затем вы можете отформатировать его в любом случае. Надеюсь, это поможет вам немного по-другому подумать об этом …