print - php read dom




Come salvare HTML di DOMDocument senza wrapper HTML? (16)

Sono la funzione di seguito, sto faticando a generare il DOMDocument senza aggiungere i wrapper di tag XML, HTML, body e p prima dell'output del contenuto. La soluzione suggerita:

$postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0));

Funziona solo quando il contenuto non contiene elementi a livello di blocco al suo interno. Tuttavia, quando lo fa, come nell'esempio seguente con l'elemento h1, l'output risultante da saveXML viene troncato in ...

<p> Se ti piace </ p>

Ho indicato questo post come una possibile soluzione alternativa, ma non riesco a capire come implementarlo in questa soluzione (vedi i tentativi commentati qui sotto).

Eventuali suggerimenti?

function rseo_decorate_keyword($postarray) {
    global $post;
    $keyword = "Jasmine Tea"
    $content = "If you like <h1>jasmine tea</h1> you will really like it with Jasmine Tea flavors. This is the last ocurrence of the phrase jasmine tea within the content. If there are other instances of the keyword jasmine tea within the text what happens to jasmine tea."
    $d = new DOMDocument();
    @$d->loadHTML($content);
    $x = new DOMXpath($d);
    $count = $x->evaluate("count(//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and (ancestor::b or ancestor::strong)])");
    if ($count > 0) return $postarray;
    $nodes = $x->query("//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and not(ancestor::h1) and not(ancestor::h2) and not(ancestor::h3) and not(ancestor::h4) and not(ancestor::h5) and not(ancestor::h6) and not(ancestor::b) and not(ancestor::strong)]");
    if ($nodes && $nodes->length) {
        $node = $nodes->item(0);
        // Split just before the keyword
        $keynode = $node->splitText(strpos($node->textContent, $keyword));
        // Split after the keyword
        $node->nextSibling->splitText(strlen($keyword));
        // Replace keyword with <b>keyword</b>
        $replacement = $d->createElement('strong', $keynode->textContent);
        $keynode->parentNode->replaceChild($replacement, $keynode);
    }
$postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0));
//  $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->item(1));
//  $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->childNodes);
return $postarray;
}

È il 2017 e per questa domanda del 2011 non mi piacciono le risposte. Un sacco di regex, grandi classi, loadXML, ecc ...

Soluzione semplice che risolve i problemi noti:

$dom = new DOMDocument();
$dom->loadHTML( '<html><body>'.mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8').'</body></html>' , LIBXML_HTML_NODEFDTD);
$html = substr(trim($dom->saveHTML()),12,-14);

Facile, semplice, solido, veloce. Questo codice funzionerà per quanto riguarda i tag HTML e la codifica come:

$html = '<p>äöü</p><p>ß</p>';

Se qualcuno trova un errore, per favore dillo, lo userò io stesso.

Modifica , Altre opzioni valide che funzionano senza errori (molto simili a quelle già fornite):

@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$saved_dom = trim($dom->saveHTML());
$start_dom = stripos($saved_dom,'<body>')+6;
$html = substr($saved_dom,$start_dom,strripos($saved_dom,'</body>') - $start_dom );

Potresti aggiungere tu stesso il corpo per prevenire qualsiasi cosa strana sulla pelliccia.

Opzione Thirt:

 $mock = new DOMDocument;
 $body = $dom->getElementsByTagName('body')->item(0);
  foreach ($body->childNodes as $child){
     $mock->appendChild($mock->importNode($child, true));
  }
$html = trim($mock->saveHTML());

Avevo anche questo requisito e mi è piaciuta la soluzione postata da Alex sopra. Ci sono un paio di problemi, però - se l'elemento <body> contiene più di un elemento figlio, il documento risultante conterrà solo il primo elemento figlio di <body> , non tutti. Inoltre, avevo bisogno dello stripping per gestire le cose in modo condizionale - solo quando avevi un documento con le intestazioni HTML. Quindi l'ho rifinito come segue. Invece di rimuovere <body> , l'ho trasformato in <div> e ho rimosso la dichiarazione XML e <html> .

function strip_html_headings($html_doc)
{
    if (is_null($html_doc))
    {
        // might be better to issue an exception, but we silently return
        return;
    }

    // remove <!DOCTYPE 
    if (!is_null($html_doc->firstChild) &&
        $html_doc->firstChild->nodeType == XML_DOCUMENT_TYPE_NODE)
    {
        $html_doc->removeChild($html_doc->firstChild);     
    }

    if (!is_null($html_doc->firstChild) &&
        strtolower($html_doc->firstChild->tagName) == 'html' &&
        !is_null($html_doc->firstChild->firstChild) &&
        strtolower($html_doc->firstChild->firstChild->tagName) == 'body')
    {
        // we have 'html/body' - replace both nodes with a single "div"        
        $div_node = $html_doc->createElement('div');

        // copy all the child nodes of 'body' to 'div'
        foreach ($html_doc->firstChild->firstChild->childNodes as $child)
        {
            // deep copies each child node, with attributes
            $child = $html_doc->importNode($child, true);
            // adds node to 'div''
            $div_node->appendChild($child);
        }

        // replace 'html/body' with 'div'
        $html_doc->removeChild($html_doc->firstChild);
        $html_doc->appendChild($div_node);
    }
}

Forse sono troppo tardi Ma forse qualcuno (come me) ha ancora questo problema.
Quindi, nessuno dei precedenti ha funzionato per me. Poiché $ dom-> loadHTML chiude anche i tag aperti, non solo aggiunge tag html e body.
Quindi aggiungere un elemento <div> non funziona per me, perché a volte mi piace 3-4 div non chiuso nel pezzo html.
La mia soluzione:

1.) Aggiungi un marcatore da tagliare, quindi carica il pezzo html

$html_piece = "[MARK]".$html_piece."[/MARK]";
$dom->loadHTML($html_piece);

2.) do whatever you want with the document
3.) save html

$new_html_piece = $dom->saveHTML();

4.) before you return it, remove < p >< /p > tags from marker, strangely it is only appear on [MARK] but not on [/MARK]...!?

$new_html_piece = preg_replace( "/<p[^>]*?>(\[MARK\]|\s)*?<\/p>/", "[MARK]" , $new_html_piece );

5.) remove everything before and after marker

$pattern_contents = '{\[MARK\](.*?)\[\/MARK\]}is';
if (preg_match($pattern_contents, $new_html_piece, $matches)) {
    $new_html_piece = $matches[1];
}

6.) return it

return $new_html_piece;

It would be a lot easier if LIBXML_HTML_NOIMPLIED worked for me. It schould, but it is not. PHP 5.4.17, libxml Version 2.7.8.
I find really strange, I use the HTML DOM parser and then, to fix this "thing" I have to use regex... The whole point was, not to use regex ;)


Ho PHP 5.3 e le risposte qui non hanno funzionato per me.

$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); Ho sostituito tutti i documenti con solo il primo figlio, ho avuto molti paragrafi e solo il primo è stato salvato, ma la soluzione mi ha dato un buon punto di partenza per scrivere qualcosa senza regex Ho lasciato alcuni commenti e sono abbastanza sicuro che questo può essere migliorato ma se qualcuno ha lo stesso problema come me può essere un buon punto di partenza.

function extractDOMContent($doc){
    # remove <!DOCTYPE
    $doc->removeChild($doc->doctype);

    // lets get all children inside the body tag
    foreach ($doc->firstChild->firstChild->childNodes as $k => $v) {
        if($k !== 0){ // don't store the first element since that one will be used to replace the html tag
            $doc->appendChild( clone($v) ); // appending element to the root so we can remove the first element and still have all the others
        }
    }
    // replace the body tag with the first children
    $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);
    return $doc;
}

Quindi potremmo usarlo in questo modo:

$doc = new DOMDocument();
$doc->encoding = 'UTF-8';
$doc->loadHTML('<p>Some html here</p><p>And more html</p><p>and some html</p>');
$doc = extractDOMContent($doc);

Nota che appendChild accetta un DOMNode quindi non abbiamo bisogno di creare nuovi elementi, possiamo semplicemente riutilizzare quelli esistenti che implementano DOMNode come DOMElement questo può essere importante per mantenere il codice "sano" quando si manipolano più documenti HTML / XML


L'aggiunta del tag <meta> attiverà il comportamento di riparazione di DOMDocument . La parte buona è che non è necessario aggiungere quel tag. Se non vuoi usare una codifica di tua scelta, basta passarla come argomento costruttore.

http://php.net/manual/en/domdocument.construct.php

$doc = new DOMDocument('1.0', 'UTF-8');
$node = $doc->createElement('div', 'Hello World');
$doc->appendChild($node);
echo $doc->saveHTML();

Produzione

<div>Hello World</div>

Grazie a @Bart


La risposta di Alex è corretta, ma potrebbe causare il seguente errore sui nodi vuoti:

L'argomento 1 passato a DOMNode :: removeChild () deve essere un'istanza di DOMNode

Ecco che arriva la mia piccola mod:

    $output = '';
    $doc = new DOMDocument();
    $doc->loadHTML($htmlString); //feed with html here

    if (isset($doc->firstChild)) {

        /* remove doctype */

        $doc->removeChild($doc->firstChild);

        /* remove html and body */

        if (isset($doc->firstChild->firstChild->firstChild)) {
            $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);
            $output = trim($doc->saveHTML());
        }
    }
    return $output;

Aggiungere il trim () è anche una buona idea per rimuovere gli spazi bianchi.


Mi sono imbattuto in questo argomento per trovare un modo per rimuovere il wrapper HTML. Utilizzo di LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD funziona alla grande, ma ho un problema con utf-8. Dopo molti sforzi ho trovato una soluzione. Lo mando qui sotto per chiunque abbia lo stesso problema.

Il problema causato da <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Il problema:

$dom = new DOMDocument();
$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $document, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$dom->saveHTML();

Soluzione 1:

$dom->loadHTML(mb_convert_encoding($document, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    $dom->saveHTML($dom->documentElement));

Soluzione 2:

$dom->loadHTML($document, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
utf8_decode($dom->saveHTML($dom->documentElement));

Nessuna delle altre soluzioni al momento della stesura di questo documento (giugno 2012) è stata in grado di soddisfare completamente le mie esigenze, quindi ne ho scritto una che gestisce i seguenti casi:

  • Accetta contenuti di testo semplice che non hanno tag né contenuti HTML.
  • Non aggiunge alcun tag (compresi i tag <doctype> , <xml> , <html> , <body> e <p> )
  • Lascia tutto avvolto in <p> da solo.
  • Lascia il testo vuoto da solo.

Quindi ecco una soluzione che risolve questi problemi:

class DOMDocumentWorkaround
{
    /**
     * Convert a string which may have HTML components into a DOMDocument instance.
     *
     * @param string $html - The HTML text to turn into a string.
     * @return \DOMDocument - A DOMDocument created from the given html.
     */
    public static function getDomDocumentFromHtml($html)
    {
        $domDocument = new DOMDocument();

        // Wrap the HTML in <div> tags because loadXML expects everything to be within some kind of tag.
        // LIBXML_NOERROR and LIBXML_NOWARNING mean this will fail silently and return an empty DOMDocument if it fails.
        $domDocument->loadXML('<div>' . $html . '</div>', LIBXML_NOERROR | LIBXML_NOWARNING);

        return $domDocument;
    }

    /**
     * Convert a DOMDocument back into an HTML string, which is reasonably close to what we started with.
     *
     * @param \DOMDocument $domDocument
     * @return string - The resulting HTML string
     */
    public static function getHtmlFromDomDocument($domDocument)
    {
        // Convert the DOMDocument back to a string.
        $xml = $domDocument->saveXML();

        // Strip out the XML declaration, if one exists
        $xmlDeclaration = "<?xml version=\"1.0\"?>\n";
        if (substr($xml, 0, strlen($xmlDeclaration)) == $xmlDeclaration) {
            $xml = substr($xml, strlen($xmlDeclaration));
        }

        // If the original HTML was empty, loadXML collapses our <div></div> into <div/>. Remove it.
        if ($xml == "<div/>\n") {
            $xml = '';
        }
        else {
            // Remove the opening <div> tag we previously added, if it exists.
            $openDivTag = "<div>";
            if (substr($xml, 0, strlen($openDivTag)) == $openDivTag) {
                $xml = substr($xml, strlen($openDivTag));
            }

            // Remove the closing </div> tag we previously added, if it exists.
            $closeDivTag = "</div>\n";
            $closeChunk = substr($xml, -strlen($closeDivTag));
            if ($closeChunk == $closeDivTag) {
                $xml = substr($xml, 0, -strlen($closeDivTag));
            }
        }

        return $xml;
    }
}

Ho anche scritto alcuni test che sarebbero vissuti nella stessa classe:

public static function testHtmlToDomConversions($content)
{
    // test that converting the $content to a DOMDocument and back does not change the HTML
    if ($content !== self::getHtmlFromDomDocument(self::getDomDocumentFromHtml($content))) {
        echo "Failed\n";
    }
    else {
        echo "Succeeded\n";
    }
}

public static function testAll()
{
    self::testHtmlToDomConversions('<p>Here is some sample text</p>');
    self::testHtmlToDomConversions('<div>Lots of <div>nested <div>divs</div></div></div>');
    self::testHtmlToDomConversions('Normal Text');
    self::testHtmlToDomConversions(''); //empty
}

Puoi verificare che funzioni per te. DomDocumentWorkaround::testAll() restituisce questo:

    Succeeded
    Succeeded
    Succeeded
    Succeeded

Se la soluzione per le bandiere fornita da Alessandro Vendruscolo non funziona, puoi provare questo:

$dom = new DOMDocument();
$dom->loadHTML($content);

//do your stuff..

$finalHtml = '';
$bodyTag = $dom->documentElement->getElementsByTagName('body')->item(0);
foreach ($bodyTag->childNodes as $rootLevelTag) {
    $finalHtml .= $dom->saveHTML($rootLevelTag);
}
echo $finalHtml;

$bodyTag conterrà il codice HTML completo elaborato senza tutti quei $bodyTag HTML, ad eccezione del tag <body> , che è la radice del contenuto. Quindi puoi usare una regex o una funzione trim per rimuoverla dalla stringa finale (dopo saveHTML ) o, come nel caso precedente, iterare su tutti i suoi childen, salvandone il contenuto in una variabile temporanea $finalHtml e restituirla (cosa credo di essere più sicuro).


Sono un po 'in ritardo nel club ma non volevo condividere un metodo che ho scoperto. Prima di tutto ho le giuste versioni per loadHTML () per accettare queste belle opzioni, ma LIBXML_HTML_NOIMPLIED non ha funzionato sul mio sistema. Anche gli utenti segnalano problemi con il parser (ad esempio here e here ).

La soluzione che ho creato in realtà è piuttosto semplice.

L'HTML da caricare viene inserito in un elemento <div> modo che abbia un contenitore contenente tutti i nodi da caricare.

Quindi questo elemento contenitore viene rimosso dal documento (ma il DOMElement di esso esiste ancora).

Quindi tutti i bambini diretti dal documento vengono rimossi. Questo include qualsiasi <html> , <head> e <body> LIBXML_HTML_NOIMPLIED (in pratica LIBXML_HTML_NOIMPLIED opzione LIBXML_HTML_NOIMPLIED ) e la dichiarazione <!DOCTYPE html ... loose.dtd"> (in pratica LIBXML_HTML_NODEFDTD ).

Quindi tutti i figli diretti del contenitore vengono nuovamente aggiunti al documento e possono essere stampati.

$str = '<p>Lorem ipsum dolor sit amet.</p><p>Nunc vel vehicula ante.</p>';

$doc = new DOMDocument();

$doc->loadHTML("<div>$str</div>");

$container = $doc->getElementsByTagName('div')->item(0);

$container = $container->parentNode->removeChild($container);

while ($doc->firstChild) {
    $doc->removeChild($doc->firstChild);
}

while ($container->firstChild ) {
    $doc->appendChild($container->firstChild);
}

$htmlFragment = $doc->saveHTML();

XPath funziona come al solito, basta fare attenzione che ora ci sono più elementi del documento, quindi non un singolo nodo root:

$xpath = new DOMXPath($doc);
foreach ($xpath->query('/p') as $element)
{   #                   ^- note the single slash "/"
    # ... each of the two <p> element
  • PHP 5.4.36-1 + deb.sury.org ~ precise + 2 (cli) (compilato: 21 dic 2014 20:28:53)

Un trucco saveHTML è usare loadXML e quindi saveHTML . I tag html e body vengono inseriti nella fase di load , non nella fase di save .

$dom = new DOMDocument;
$dom->loadXML('<p>My DOMDocument contents are here</p>');
echo $dom->saveHTML();

NB che questo è un po 'hacky e dovresti usare la risposta di Jonah se riesci a farlo funzionare.


Usa questa funzione

$layout = preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '', $layout);

Va bene ho trovato una soluzione più elegante, ma è solo noioso:

$d = new DOMDocument();
@$d->loadHTML($yourcontent);
...
// do your manipulation, processing, etc of it blah blah blah
...
// then to save, do this
$x = new DOMXPath($d);
$everything = $x->query("body/*"); // retrieves all elements inside body tag
if ($everything->length > 0) { // check if it retrieved anything in there
      $output = '';
      foreach ($everything as $thing) {
           $output .= $d->saveXML($thing);
      }
      echo $output; // voila, no more annoying html wrappers or body tag
}

Bene, spero che questo non tralascia nulla e aiuti qualcuno?


il mio server ha php 5.3 e non può aggiornare così queste opzioni

LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD

non sono per me

Per risolvere questo dico alla funzione SaveXML per stampare l'elemento Body e quindi basta sostituire il "body" con "div"

ecco il mio codice, spero che aiuti qualcuno:

<? 
$html = "your html here";
$tabContentDomDoc = new DOMDocument();
$tabContentDomDoc->loadHTML('<?xml encoding="UTF-8">'.$html);
$tabContentDomDoc->encoding = 'UTF-8';
$tabContentDomDocBody = $tabContentDomDoc->getElementsByTagName('body')->item(0);
if(is_object($tabContentDomDocBody)){
    echo (str_replace("body","div",$tabContentDomDoc->saveXML($tabContentDomDocBody)));
}
?>

l'utf-8 è per il supporto ebraico.


For anyone using Drupal, there's a built in function to do this:

https://api.drupal.org/api/drupal/modules!filter!filter.module/function/filter_dom_serialize/7.x

Code for reference:

function filter_dom_serialize($dom_document) {
  $body_node = $dom_document->getElementsByTagName('body')->item(0);
  $body_content = '';

  if ($body_node !== NULL) {
    foreach ($body_node->getElementsByTagName('script') as $node) {
      filter_dom_serialize_escape_cdata_element($dom_document, $node);
    }

    foreach ($body_node->getElementsByTagName('style') as $node) {
      filter_dom_serialize_escape_cdata_element($dom_document, $node, '/*', '*/');
    }

    foreach ($body_node->childNodes as $child_node) {
      $body_content .= $dom_document->saveXML($child_node);
    }
    return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content);
  }
  else {
    return $body_content;
  }
}

I am struggling with this on RHEL7 running PHP 5.6.25 and LibXML 2.9. (Old stuff in 2018, I know, but that is Red Hat for you.)

I have found that the much upvoted solution suggested by Alessandro Vendruscolo breaks the HTML by rearranging tags. Ie:

<p>First.</p><p>Second.</p>'

diventa:

<p>First.<p>Second.</p></p>'

This goes for both the options he suggests you use: LIBXML_HTML_NOIMPLIED and LIBXML_HTML_NODEFDTD .

The solution suggested by Alex goes half way to solve it, but it does not work if <body> has more than one child node.

The solution that works for me is the follwing:

First, to load the DOMDocument, I use:

$doc = new DOMDocument()
$doc->loadHTML($content);

To save the document after massaging the DOMDocument, I use:

// remove <!DOCTYPE 
$doc->removeChild($doc->doctype);  
$content = $doc->saveHTML();
// remove <html><body></body></html> 
$content = str_replace('<html><body>', '', $content);
$content = str_replace('</body></html>', '', $content);

I am the first to agree that this this is not a very elegant solution - but it works.





domdocument