我一直在使用CKEditor所见即所得的编辑器用于允许用户使用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]
问题现在是:是否有一种简单的方法(算法/库/等)来清理将要生成的凌乱(与原始HTML一样混乱)bbcode? p >
再次感谢
答案 0 :(得分:20)
到目前为止,最好的解决方案是使用HTML Tidy
http://tidy.sourceforge.net/
除了转换文档格式之外,Tidy还可以通过使用clean选项自动将已弃用的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>';
如果你跑
$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);
}
=============================================== =
=============================================== =
根据您的上一条评论,您希望保留折旧样式。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] );
}
}
答案 1 :(得分:5)
您应该查看HTMLPurifier,它是解析HTML并从中删除不必要和不安全内容的绝佳工具。查看删除空跨配置和东西。配置我承认它可能是一个野兽,但这只是因为它是如此多才多艺。
它也很重,所以你想要保存数据库的输出(而不是从数据库中读取原始数据,然后每次都用净化器解析它。
答案 2 :(得分:5)
这是一个使用浏览器获取嵌套元素属性的解决方案。无需级联属性,因为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”];
是使用这里提到的解决方案:
Can jQuery get all CSS styles associated with an element?
答案 3 :(得分:2)
我没有时间完成这个......也许其他人可以提供帮助。这个javascript也删除了完全重复的标签和不允许的标签......
有一些问题/需要做的事情,
1)需要关闭再生标签
2)如果标签名称&amp;属性与该节点子节点中的另一个属性相同,因此它不够“智能”,足以删除所有不必要的标记
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>
答案 4 :(得分:1)
它可能无法准确解决您的确切问题,但我所做的就是完全删除所有HTML标记,仅保留痛苦文本和换行符。
完成此操作后,切换到markdown我们的bbcode以更好地格式化您的评论。 WYSIWYG很少有用。
之所以如此,是因为你说你在评论中所拥有的只是表现数据,坦率地说,并不是那么重要。
答案 5 :(得分:1)
Cleanup HTML折叠标签,这似乎是您所要求的。但是,它会创建一个经过验证的HTML文档,并将CSS移动到内联样式。许多其他HTML格式化程序不会这样做,因为它会更改HTML文档的结构。
答案 6 :(得分:0)
我记得Adobe(Macromedia)Dreamweaver,至少有些旧版本有一个选项,&#39;清理HTML&#39;还有一个&#39;清理单词html&#39;从任何网页上删除多余的标签等。
答案 7 :(得分:0)
我知道你正在寻找一个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
}
}
}
答案 8 :(得分:0)
不是浪费宝贵的服务器时间来解析错误的HTML,而是建议您修复问题的根源。
一个简单的解决方案是限制每个评论员可以制作的字符,以包括整个html字符数,而不仅仅是文本计数(至少会阻止无限大的嵌套标记)。
你可以通过允许用户在HTML视图和文本视图之间切换来改进这一点 - 我确信大多数人在HTML视图中会看到一堆垃圾,只需按CTRL + A&amp; DEL它。
我认为最好是你有自己的格式化字符,你会解析并用格式替换,例如堆栈溢出有**bold text**
,海报可见。或者只是BB代码可以做到,可以看到海报。
答案 9 :(得分:0)
尽量不用DOM解析HTML,但可能使用SAX解析HTML(http://www.brainbell.com/tutorials/php/Parsing_XML_With_SAX.htm)
SAX从头开始解析文档,并发送“元素开始”和“元素结束”之类的事件来调用您定义的回调函数
然后你可以为所有事件构建一种堆栈如果你有文本,你可以保存堆栈对该文本的影响。
之后,您将处理堆栈以构建仅具有所需效果的新HTML。
答案 10 :(得分:0)
如果你想使用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>
然后你可以随意格式化它。希望能帮助你对它有所不同......