36

私は、ユーザーがHTMLエディターを使用してコメントを追加できるWebサイトにCKEditorwysiwygエディターを使用していますデータベースに非常に冗長なネストされた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をクリーンアップする簡単な方法(アルゴリズム/ライブラリなど)はありますか?

再度、感謝します

4

11 に答える 11

22

序章

これまでに見た最良の解決策は、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);
}

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

編集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] );
}

}
于 2012-04-25T01:28:08.540 に答える
6

これは、ブラウザを使用してネストされた要素のプロパティを取得するソリューションです。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スタイルを取得できますか?

于 2012-04-25T06:14:45.630 に答える
5

HTMLPurifierを調べる必要があります。これは、HTMLを解析し、不要で安全でないコンテンツをHTMLから削除するための優れたツールです。空のスパン構成などの削除を調べてください。私が認める構成を行うのは少し野獣かもしれませんが、それは非常に用途が広いからです。

また、非常に重いので、その出力をデータベースに保存することをお勧めします(データベースからrawを読み取り、毎回ピューリファイヤーで解析するのとは対照的です。

于 2012-04-20T14:30:42.460 に答える
2

私はこれを終える時間がありません...多分誰か他の人が助けることができます。この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>
于 2012-04-23T05:08:21.030 に答える
1

それはあなたの正確な問題に正確に対処しないかもしれませんが、私があなたの代わりにしたことは、単にすべてのHTMLタグを完全に削除し、痛みのあるテキストと改行だけを保持することです。

それが終わったら、あなたのコメントをより良くフォーマットするために私たちのbbcodeをマークダウンするように切り替えてください。WYSIWYGが役立つことはめったにありません。

その理由は、コメントにあるのはプレゼンテーションデータだけだと言ったからです。率直に言って、それほど重要ではありません。

于 2012-04-23T05:39:41.357 に答える
1

クリーンアップHTMLは、あなたが求めているもののように見えるタグを折りたたむ。ただし、CSSをインラインスタイルに移動して検証済みのHTMLドキュメントを作成します。他の多くのHTMLフォーマッタは、HTMLドキュメントの構造を変更するため、これを行いません。

于 2012-04-25T17:27:23.797 に答える
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>

その後、必要に応じてフォーマットできます。それがあなたがそれについて少し違った考え方をするのを助けることを願っています...

于 2012-04-25T17:13:39.467 に答える
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
        }
    } 
} 
于 2012-04-20T14:55:14.497 に答える
0

Adobe(Macromedia)Dreamweaverには、少なくとも少し古いバージョンには、「HTMLのクリーンアップ」オプションと「HTMLのクリーンアップ」オプションがあり、冗長なタグなどをWebページから削除したことを覚えています。

于 2012-04-20T14:50:21.987 に答える
0

貴重なサーバーの時間を無駄にして悪いHTMLを解析するのではなく、代わりに問題の根本を修正することをお勧めします。

簡単な解決策は、各コメント投稿者が作成できる文字を制限して、テキスト数だけでなくhtml文字数全体を含めることです(少なくとも、ネストされたタグが無限に大きくなるのを防ぐことができます)。

ユーザーがHTMLビューとテキストビューを切り替えることができるようにすることで、これを改善できます。ほとんどの人は、HTMLビューにいるときに大量のジャンクを表示し、Ctrl+AとDELを押すだけです。

解析してフォーマットに置き換える独自のフォーマット文字があれば、それが最善だと思います。つまり、スタックオーバーフロー**bold text**のように、ポスターに表示されます。または、ポスターに見えるように、BBコードだけで十分です。

于 2012-04-22T17:26:53.067 に答える
0

HTMLをDOMで解析するのではなく、SAXで解析するようにしてください(http://www.brainbell.com/tutorials/php/Parsing_XML_With_SAX.htm)

SAXはドキュメントを最初から解析し、「要素の開始」や「要素の終了」などのイベントを送信して、定義したコールバック関数を呼び出します。

次に、すべてのイベントに対して一種のスタックを構築できます。テキストがある場合は、そのテキストに対するスタックの影響を保存できます。

その後、スタックを処理して、必要な効果のみを備えた新しいHTMLを構築します。

于 2012-04-23T17:27:20.647 に答える