Nettoyage du HTML en supprimant les balises de formatage supplémentaires / redondantes

j'ai été en utilisant CKEditor de l'éditeur wysiwyg pour un site web où les utilisateurs sont autorisés à utiliser l'éditeur HTML pour ajouter des commentaires. J'ai fini par avoir un code HTML imbriqué extrêmement redondant dans ma base de données qui ralentit la visualisation/édition de ces commentaires.

j'ai des commentaires qui ressemblent à ceci (c'est un tout petit exemple. J'ai des commentaires avec plus de 100 étiquettes imbriquées):

<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>

My les questions sont:

  • y a-t-il une bibliothèque/un code/logiciel capable de faire un nettoyage intelligent (c.-à-d. sensible au format) du code HTML, en supprimant toutes les balises redondantes qui n'ont aucun effet sur le formatage (parce qu'elles sont remplacées par des balises internes) ? J'ai essayé de nombreuses solutions en ligne existantes (telles que HTML Tidy ). Aucun d'eux ne fait ce que je veux.

  • si ce n'est pas le cas, je vais devoir écrire un code pour l'analyse et le nettoyage HTML. Je prévois d'utiliser PHP Simple HTML DOM pour traverser l'arbre HTML et trouver toutes les balises qui n'ont pas d'effet. Suggérez-vous un autre analyseur HTML plus adapté à mon usage?

Merci

.

mise à jour:

j'ai écrit un code pour analyser le code HTML que j'ai. Toutes les balises HTML que j'AI sont:

  • <span> avec des styles pour font-size et/ou color
  • <font> avec les attributs color et/ou size
  • <a> pour les liens (avec href )
  • <strong>
  • <p> (étiquette simple pour envelopper le commentaire entier)
  • <u>

je peux facilement écrire un code pour convertir le code HTML en bbcode (par ex. [b] , [color=blue] , [size=3] , etc). Donc je au-dessus de HTML deviendra quelque chose comme:

[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]

la question est maintenant: y a-t-il un moyen facile (algorithme/bibliothèque/etc) pour nettoyer le désordre (aussi salissant que ce code HTML) bbcode original qui sera généré?

merci encore

31
demandé sur Ozzy 2012-04-20 18:26:58

11 réponses

Introduction

la meilleure solution a vu jusqu'à présent est d'utiliser HTML Tidy http://tidy.sourceforge.net /

au-delà de la conversion du format d'un document, Tidy est également capable de convertir automatiquement les balises HTML obsolètes en leurs équivalents en feuilles de style en cascade (CSS) en utilisant l'option clean. La sortie générée contient une déclaration de style en ligne.

il assure également que le document HTML est xhtml compatible

exemple

$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>';

si vous courez

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

sortie

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

vous pouvez obtenir le CSS

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

sortie

<style type="text/css">
    span.c3 {
        font-size: 14px
    }

    span.c2 {
        color: #006400;
        font-size: 16px
    }

    span.c1 {
        color: #006400;
        font-size: 14px
    }
</style>

Our the FULL HTML

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

sortie

<!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>

Fonction Utilisée

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);
}

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

Edit 1 : Sale Hack (Non Recommandé)

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

basé sur votre dernier commentaire c'est comme si vous vouliez conserver le style déprécié .. HTML Tidy peut ne pas vous permettre de le faire depuis son depreciated mais vous pouvez le faire

$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 ) . ";\">";
}

sortie

<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>

Classe Utilisée

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

}
20
répondu Baba 2012-04-27 12:07:10

vous devriez regarder dans HTMLPurifier , c'est un excellent outil pour analyser HTML et supprimer le contenu inutile et dangereux de celui-ci. Regardez dans la suppression des configs empty spans et autres. Il peut être un peu bête pour configurer je l'avoue, mais c'est seulement parce qu'il est si polyvalent.

c'est aussi assez lourd, donc vous voulez enregistrer la sortie de celui-ci la base de données (par opposition à la lecture de la raw à partir de la base de données et ensuite l'analyse avec purifier chaque fois.

5
répondu Dunhamzzz 2012-04-20 14:30:42

Voici une solution qui utilise le navigateur pour obtenir l'élément imbriqué propriétés. Pas besoin de faire passer les propriétés en cascade, puisque les styles calculés css sont prêts à être lus par le navigateur.

voici un exemple: 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;
}

une meilleure solution pour taper dans tous les attributs CSS possibles comme:

var _cssAttributeNames = ["taille de police","couleur"];

Est d'utiliser une solution comme mentionné ici: jQuery peut-il obtenir tous les styles CSS associés à un élément?

5
répondu MMeah 2017-05-23 12:01:59

je n'ai pas le temps de finir ce... peut-être quelqu'un d'autre peut vous aider. Ce javascript supprime les étiquettes identiques exactes et les étiquettes non autorisées aussi...

il y a quelques problèmes/choses à faire,

1) les étiquettes régénérées doivent être fermées

2) il ne supprimera une balise que si la balise-nom & attributs sont identiques à un autre dans les noeuds enfants, de sorte qu'il n'est pas assez "intelligent" pour supprimer toutes les balises inutiles.

3) il va regarder à travers les variables CSS autorisées et extraire toutes ces valeurs d'un élément, puis l'écrire à la sortie HTML, donc par exemple:

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

sera traduit en:

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

Code:

<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>
2
répondu Ozzy 2012-04-23 05:08:21

il se peut que cela ne résolve pas exactement votre problème, mais ce que j'aurais fait à votre place est simplement d'éliminer toutes les balises HTML complètement, ne conserver que le texte de la douleur et les sauts de ligne.

après cela a été fait, passer à markdown notre bbcode pour mieux formater vos commentaires. Un WYSIWYG est rarement utile.

la raison pour cela est parce que vous avez dit que tout ce que vous aviez dans les commentaires Est des données de présentation, qui franchement, n'est pas si important.

1
répondu Madara Uchiha 2012-04-23 05:39:41

Nettoyage HTML effondre les étiquettes qui semblent être ce que vous demandez. Cependant, il crée un document HTML validé avec CSS déplacé vers les styles inline. Beaucoup d'autres formateurshtml ne le feront pas parce qu'ils modifient la structure du document HTML.

1
répondu Jason 2012-04-25 17:27:23

je me souviens Qu'Adobe (Macromedia) Dreamweaver, au moins de vieilles versions avaient une option, "Nettoyer HTML", et aussi un "Nettoyer mot html" pour supprimer les étiquettes redondantes etc de toute page web.

0
répondu Manoj Solanki 2012-04-20 14:50:21

je sais que vous cherchez un nettoyeur de DOM HTML, mais peut-être que js peut vous aider?

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
        }
    } 
} 
0
répondu squarephoenix 2012-04-20 14:55:14

plutôt que de gaspiller votre précieux temps de serveur à analyser le mauvais HTML, je vous suggérerais de corriger la racine du problème à la place.

une solution simple serait de limiter les caractères que chaque commentateur pourrait faire pour inclure le compte de caractères html entier par opposition à juste le compte de texte (au moins qui arrêterait infinately-grandes étiquettes emboîtées).

vous pourriez améliorer cela en permettant à l'utilisateur de passer entre HTML-view et text-view - je suis sûr que la plupart les gens verraient une charge de camelote dans la vue HTML et simplement CTRL+A & DEL it.

je pense qu'il serait préférable si vous aviez vos propres chars de formatage que vous parsèteriez et remplaceriez par le formatage c.-à-d. comme stack-overflow a **bold text** , visible à l'affiche. Ou juste un code BB ferait l'affaire, visibile à l'affiche.

0
répondu Ozzy 2012-04-22 17:26:53

essayez de ne pas Parser le HTML avec DOM mais peut-être avec SAX (http://www.brainbell.com/tutorials/php/Parsing_XML_With_SAX.htm)

SAX analyse un document depuis le début et envoie des événements comme 'start of element' et 'end of' element 'pour appeler les fonctions de callback que vous définissez

alors vous pouvez construire une sorte de pile pour tous les événements Si vous avez du texte, vous pouvez enregistrer l'effet de votre pile sur ce texte.

après cela vous traitez la pile pour construire un nouveau HTML avec seulement l'effet que vous voulez.

0
répondu HerrSerker 2012-04-23 17:27:20

si vous voulez utiliser jQuery, essayez ceci:

<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);
    }        
});

Vous obtenez ce résultat:

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

vous pouvez alors le formater comme vous voulez. L'espoir qui vous aide à penser un peu différemment à ce sujet...

0
répondu Paul 2012-04-25 17:13:39