Очистка HTML путем удаления дополнительных / избыточных тегов форматирования
я использую CKEditor WYSIWYG editor для веб-сайта, где пользователям разрешено использовать редактор 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 простой 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]
вопрос теперь: есть ли простой способ (алгоритм/библиотека/и т. д.) очистить беспорядочный (такой же беспорядочный, как и исходный HTML) bbcode, который будет сгенерирован?
еще раз спасибо
11 ответов
введение
лучшее решение пока видела используя HTML Tidy
http://tidy.sourceforge.net/
помимо преобразования формата документа, Tidy также может автоматически конвертировать устаревшие HTML-теги в их каскадные таблицы стилей (CSS) с помощью опции clean. Сгенерированный вывод содержит встроенное объявление стиля.
это также гарантирует, что 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>';
если вы запустите
$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>
вы должны посмотреть в HTMLPurifier, это отличный инструмент для разбора HTML и удаления из него ненужного и небезопасного контента. Посмотрите на удаление пустых конфигураций spans и прочее. Это может быть немного зверь, чтобы настроить я признаю, но это только потому, что он настолько универсален.
Он также довольно тяжелый, поэтому вы хотите сохранить его вывод в базе данных (в отличие от чтения 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 = ["размер шрифта", "цвет"];
Использовать решение, как указано здесь:
Can 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,"<")).replace(/>/g,">")+"</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, я бы предложил вам исправить корень проблемы.
простым решением было бы ограничить символы, которые каждый комментатор мог бы сделать, чтобы включить весь счетчик символов html, а не только количество текста (по крайней мере, это остановило бы бесконечно большие вложенные теги).
вы можете улучшить это, разрешив пользователю переключаться между HTML-view и text-view - я уверен, что большинство людей увидят нагрузку нежелательной, когда в представлении HTML и просто CTRL+A & DEL.
Я думаю, было бы лучше, если бы у вас были свои собственные символы форматирования, которые вы бы проанализировали и заменили форматированием, т. е. как stack-overflow имеет **bold text**
, виден плакат. Или просто BB-код, visibile к плакату.
попробуйте не анализировать 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>
вы тогда смогла бы формат вы хотите. Надеюсь, это поможет вам думать об этом немного по-другому...