php - texto - string strip_tags
¿Cómo guardar HTML de DOMDocument sin envoltorio HTML? (24)
Soy la función a continuación, me cuesta publicar el DOMDocument sin que añada los envoltorios XML, HTML, body y p antes del resultado del contenido. La solución sugerida:
$postarray[''post_content''] = $d->saveXML($d->getElementsByTagName(''p'')->item(0));
Solo funciona cuando el contenido no tiene elementos de nivel de bloque dentro de él. Sin embargo, cuando lo hace, como en el ejemplo siguiente con el elemento h1, la salida resultante de saveXML se trunca en ...
<p> Si te gusta </ p>
Me han señalado esta publicación como una solución posible, pero no puedo entender cómo implementarla en esta solución (ver los intentos comentados a continuación).
¿Alguna sugerencia?
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;
}
Agregar la etiqueta <meta>
activará el comportamiento de fijación de DOMDocument
. Lo bueno es que no necesita agregar esa etiqueta en absoluto. Si desea utilizar una codificación de su elección, simplemente páselo como un argumento de constructor.
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();
Salida
<div>Hello World</div>
Gracias a @Bart
Al igual que otros miembros, primero me deleité con la simplicidad y el increíble poder de la respuesta de @Alessandro Vendruscolo. La capacidad de simplemente pasar algunas constantes marcadas al constructor parecía demasiado buena para ser cierta. Para mí fue. Tengo las versiones correctas tanto de LibXML como de PHP, sin embargo, sin importar lo que aún agregue la etiqueta HTML a la estructura de nodos del objeto Document.
Mi solución funcionó mucho mejor que usar el ...
$html->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
Banderas o ....
# remove <!DOCTYPE
$doc->removeChild($doc->firstChild);
# remove <html><body></body></html>
$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);
Eliminación de nodo, que se vuelve desordenada sin un orden estructurado en el DOM. De nuevo, los fragmentos de código no tienen forma de predeterminar la estructura DOM.
Comencé este viaje deseando una forma simple de hacer un recorrido DOM como lo hace JQuery o, al menos de alguna manera, que tenía un conjunto de datos estructurados, ya sea individualmente enlazado, doblemente enlazado o atravesado por un nodo en árbol. No me importó cuánto tiempo podría analizar una cadena como lo hace HTML y también tener el sorprendente poder de las propiedades de la clase de entidad nodo para usar en el camino.
Hasta ahora DOMDocument Object me ha dejado con muchas ganas ... Al igual que con muchos otros programadores, parece ... Sé que he visto mucha frustración en esta pregunta, así que desde FINALMENTE .... (después de aproximadamente 30 horas de prueba y falla tipo prueba) He encontrado una manera de obtenerlo todo. Espero que esto ayude a alguien...
En primer lugar, soy cínico de TODO ... jaja ...
Me hubiera ido toda la vida antes de acordar con alguien que de todos modos se necesita una clase de terceros en este caso de uso. Mucho era y no soy fan de usar una estructura de clase de terceros, sin embargo, me topé con un gran analizador. (alrededor de 30 veces en Google antes de ceder, así que no te sientas solo si lo evitaste porque parecía cojo de manera no oficial ...)
Si está utilizando fragmentos de código y necesita el código, limpio y no afectado por el analizador de ninguna manera, sin utilizar etiquetas adicionales, utilice simplePHPParser .
Es increíble y se parece mucho a JQuery. No siempre me impresionó, pero esta clase utiliza muchas herramientas buenas y hasta ahora no he tenido errores de análisis. Soy un gran fan de poder hacer lo que hace esta clase.
Puede encontrar sus archivos para descargar simplePHPParser , sus instrucciones de inicio here y su API here . Recomiendo usar esta clase con sus métodos simples que pueden hacer un .find(".className")
la misma manera que se usaría un método de búsqueda de JQuery o incluso métodos conocidos como getElementByTagName()
o getElementById()
...
Cuando guarda un árbol de nodos en esta clase, no agrega nada. Simplemente puede decir $doc->save();
y produce todo el árbol en una cadena sin ningún problema.
Ahora usaré este analizador para todos los proyectos de ancho de banda no limitado en el futuro.
De acuerdo, encontré una solución más elegante, pero es simplemente tediosa:
$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
}
Muy bien, con suerte, esto no omite nada y ayuda a alguien?
El problema con la respuesta principal es que LIBXML_HTML_NOIMPLIED
es inestable .
Puede reordenar elementos (particularmente, mover la etiqueta de cierre del elemento superior a la parte inferior del documento), agregar etiquetas p
aleatorias y tal vez una variedad de otros problemas [1] . Puede eliminar las etiquetas html
y body
para usted, pero a costa de un comportamiento inestable. En producción, eso es una bandera roja. En breve:
No use LIBXML_HTML_NOIMPLIED
. En cambio, use substr
.
Piénsalo. Las longitudes de <html><body>
y </body></html>
son fijas y en ambos extremos del documento: sus tamaños nunca cambian y tampoco lo hacen sus posiciones. Esto nos permite usar substr
para eliminarlos:
$dom = new domDocument;
$dom->loadHTML($html, LIBXML_HTML_NODEFDTD);
echo substr($dom->saveHTML(), 12, -15); // the star of this operation
( ¡SIN EMBARGO, NO ES LA SOLUCIÓN FINAL! Vea a continuación la respuesta completa , siga leyendo para el contexto)
Cortamos 12
desde el inicio del documento porque <html><body>
= 12 caracteres ( <<>>+html+body
= 4 + 4 + 4), y retrocedemos y cortamos 15 del final porque /n</body></html>
= 15 caracteres ( /n+//+<<>>+body+html
= 1 + 2 + 4 + 4 + 4)
Tenga en cuenta que todavía uso LIBXML_HTML_NODEFDTD
omita el !DOCTYPE
de ser incluido. En primer lugar, esto simplifica la eliminación de substr
de las etiquetas HTML / BODY. En segundo lugar, no eliminamos el doctype con substr
porque no sabemos si el '' default doctype
'' siempre tendrá algo de longitud fija. Pero, lo más importante, LIBXML_HTML_NODEFDTD
impide que el analizador DOM aplique un tipo de documento no HTML5 al documento, lo que al menos impide que el analizador trate los elementos que no reconoce como texto suelto.
Sabemos a LIBXML_HTML_NODEFDTD
cierta que las etiquetas HTML / BODY son de longitudes y posiciones fijas, y sabemos que las constantes como LIBXML_HTML_NODEFDTD
nunca se eliminan sin algún tipo de aviso de LIBXML_HTML_NODEFDTD
, por lo que el método anterior debería extenderse en el futuro, PERO ...
... la única advertencia es que la implementación de DOM podría cambiar la forma en que las etiquetas HTML / BODY se colocan dentro del documento; por ejemplo, eliminar la nueva línea al final del documento, agregar espacios entre las etiquetas o agregar nuevas líneas.
Esto se puede remediar buscando las posiciones de las etiquetas de apertura y cierre para el body
, y usando esas compensaciones en cuanto a nuestras longitudes para recortar. Utilizamos strpos
y strrpos
para encontrar los desplazamientos desde la parte frontal y posterior, respectivamente:
$dom = new domDocument;
$dom->loadHTML($html, LIBXML_HTML_NODEFDTD);
$trim_off_front = strpos($dom->saveHTML(),''<body>'') + 6;
// PositionOf<body> + 6 = Cutoff offset after ''<body>''
// 6 = Length of ''<body>''
$trim_off_end = (strrpos($dom->saveHTML(),''</body>'')) - strlen($dom->saveHTML());
// ^ PositionOf</body> - LengthOfDocument = Relative-negative cutoff offset before ''</body>''
echo substr($dom->saveHTML(), $trim_off_front, $trim_off_end);
Para terminar, una repetición de la respuesta final, a prueba de futuro :
$dom = new domDocument;
$dom->loadHTML($html, LIBXML_HTML_NODEFDTD);
$trim_off_front = strpos($dom->saveHTML(),''<body>'') + 6;
$trim_off_end = (strrpos($dom->saveHTML(),''</body>'')) - strlen($dom->saveHTML());
echo substr($dom->saveHTML(), $trim_off_front, $trim_off_end);
Sin doctype, sin etiqueta html, sin etiqueta de cuerpo. Solo podemos esperar que el analizador DOM reciba pronto una nueva capa de pintura y podamos eliminar más directamente estas etiquetas no deseadas.
Encontré este tema para encontrar una manera de eliminar el contenedor HTML. Utilizando LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
funciona muy bien, pero tengo un problema con utf-8. Después de mucho esfuerzo encontré una solución. Lo publico a continuación porque cualquiera tiene el mismo problema.
El problema causado por <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
El 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();
Solución 1:
$dom->loadHTML(mb_convert_encoding($document, ''HTML-ENTITIES'', ''UTF-8''), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$dom->saveHTML($dom->documentElement));
Solución 2:
$dom->loadHTML($document, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
utf8_decode($dom->saveHTML($dom->documentElement));
Es 2017, y para esta pregunta de 2011 no me gusta ninguna de las respuestas. Un montón de expresiones regulares, grandes clases, loadXML, etc ...
Solución fácil que resuelve los problemas conocidos:
$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);
Fácil, simple, sólido, rápido. Este código funcionará con respecto a las etiquetas HTML y la codificación como:
$html = ''<p>äöü</p><p>ß</p>'';
Si alguien encuentra un error, dígale que lo usaré yo mismo.
Editar , Otras opciones válidas que funcionan sin errores (muy similares a las ya dadas):
@$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 );
Puedes agregar tu cuerpo para evitar cualquier cosa extraña en el fururo.
Thirt opción:
$mock = new DOMDocument;
$body = $dom->getElementsByTagName(''body'')->item(0);
foreach ($body->childNodes as $child){
$mock->appendChild($mock->importNode($child, true));
}
$html = trim($mock->saveHTML());
Estoy un poco tarde en el club, pero no quería compartir un método que descubrí. En primer lugar, tengo las versiones correctas para loadHTML () para aceptar estas bonitas opciones, pero LIBXML_HTML_NOIMPLIED
no funcionaba en mi sistema. También los usuarios informan problemas con el analizador (por ejemplo here y here ).
La solución que creé en realidad es bastante simple.
El HTML que se va a cargar se coloca en un elemento <div>
por lo que tiene un contenedor que contiene todos los nodos que se cargarán.
Luego, este elemento contenedor se elimina del documento (pero el elemento DOME todavía existe).
Luego se eliminan todos los hijos directos del documento. Esto incluye cualquier etiqueta <html>
, <head>
y <body>
LIBXML_HTML_NOIMPLIED
(efectivamente la opción LIBXML_HTML_NOIMPLIED
) así como la declaración <!DOCTYPE html ... loose.dtd">
(efectivamente LIBXML_HTML_NODEFDTD
).
A continuación, todos los elementos secundarios directos del contenedor se agregan al documento nuevamente y se puede generar.
$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 funciona como siempre, solo tenga cuidado de que haya varios elementos de documento ahora, de modo que no haya un único nodo raíz:
$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) (built: Dec 21 2014 20:28:53)
La respuesta de Alex es correcta, pero puede causar el siguiente error en los nodos vacíos:
El argumento 1 pasado a DOMNode :: removeChild () debe ser una instancia de DOMNode
Aquí viene mi pequeño 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;
Agregar el recorte () también es una buena idea para eliminar espacios en blanco.
Me encontré con este problema también.
Desafortunadamente, no me sentí cómoda al usar ninguna de las soluciones provistas en este hilo, así que fui a verificar una que me satisficiera.
Esto es lo que inventé y funciona sin problemas:
$domxpath = new /DOMXPath($domDocument);
/** @var /DOMNodeList $subset */
$subset = $domxpath->query(''descendant-or-self::body/*'');
$html = '''';
foreach ($subset as $domElement) {
/** @var $domElement /DOMElement */
$html .= $domDocument->saveHTML($domElement);
}
En esencia, funciona de manera similar a la mayoría de las soluciones proporcionadas aquí, pero en lugar de hacer trabajo manual usa el selector xpath para seleccionar todos los elementos dentro del cuerpo y concatena su código html.
Ninguna de las otras soluciones en el momento de escribir estas líneas (junio de 2012) fue capaz de satisfacer completamente mis necesidades, así que escribí una que maneja los siguientes casos:
- Acepta contenido de texto sin formato que no tiene etiquetas, así como contenido HTML.
- No
<doctype>
etiquetas (incluidas las etiquetas<doctype>
,<xml>
,<html>
,<body>
y<p>
) - Deja todo envuelto en
<p>
solo. - Deja el texto en blanco solo
Así que aquí hay una solución que soluciona esos problemas:
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;
}
}
También escribí algunas pruebas que vivirían en esa misma clase:
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
}
Puedes verificar que funcione para ti. DomDocumentWorkaround::testAll()
devuelve esto:
Succeeded
Succeeded
Succeeded
Succeeded
Quizás sea demasiado tarde. Pero tal vez alguien (como yo) todavía tenga este problema.
Entonces, nada de lo anterior funcionó para mí. Debido a que $ dom-> loadHTML también cierra las etiquetas abiertas, no solo agrega etiquetas html y body.
So add a < div > element is not working for me, because I have sometimes like 3-4 unclosed div in the html piece.
Mi solución:
1.) Add marker to cut, then load the html piece
$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 ;)
Si la solución de indicadores respondida por Alessandro Vendruscolo no funciona, puede intentar esto:
$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
contendrá su código HTML procesado completo sin todas las envolturas HTML, a excepción de la etiqueta <body>
, que es la raíz de su contenido. Luego puede usar una expresión regular o una función de recorte para eliminarla de la cadena final (después de saveHTML
) o, como en el caso anterior, iterar sobre todos sus hijos, guardar su contenido en una variable temporal $finalHtml
y devolverlo (qué creo estar más seguro).
Simplemente elimine los nodos directamente después de cargar el documento con loadHTML ():
# remove <!DOCTYPE
$doc->removeChild($doc->doctype);
# remove <html><body></body></html>
$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);
También tenía este requisito y me gustó la solución publicada por Alex anteriormente. Sin embargo, hay un par de problemas: si el elemento <body>
contiene más de un elemento secundario, el documento resultante solo contendrá el primer elemento secundario de <body>
, no todos. Además, necesitaba el stripping para manejar las cosas condicionalmente, solo cuando tenía un documento con los encabezados HTML. Así que lo refiné de la siguiente manera. En lugar de eliminar <body>
, lo transformé en un <div>
y eliminé la declaración XML y <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);
}
}
Tengo PHP 5.3 y las respuestas aquí no funcionaron para mí.
$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);
reemplacé todo el documento con solo el primer hijo, tenía muchos párrafos y solo se guardaba el primero, pero la solución me dio un buen punto de partida para escribir algo sin regex
Dejé algunos comentarios y estoy seguro de que esto se puede mejorar, pero si alguien tiene el mismo problema que yo, puede ser un buen punto de partida.
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;
}
Entonces podríamos usarlo así:
$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);
Tenga en cuenta que appendChild
acepta un DOMNode
por lo que no es necesario crear nuevos elementos, solo podemos reutilizar los existentes que implementan DOMNode
, como DOMElement
Esto puede ser importante para mantener el código "sano" al manipular múltiples documentos HTML / XML.
Todas estas respuestas están ahora mal , porque a partir de PHP 5.4 y Libxml 2.6 loadHTML
ahora tiene un parámetro $option
que instruye a Libxml sobre cómo debe analizar el contenido.
Por lo tanto, si cargamos el HTML con estas opciones
$html->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
al saveHTML()
no habrá doctype
, no <html>
y ningún <body>
.
LIBXML_HTML_NOIMPLIED
desactiva la adición automática de elementos html / body implicados.LIBXML_HTML_NODEFDTD
impide que seLIBXML_HTML_NODEFDTD
un doctype predeterminado cuando no se encuentra uno.
La documentación completa sobre los parámetros de Libxml está here
(Tenga en cuenta que los documentos loadHTML
dicen que se necesita Libxml 2.6, pero LIBXML_HTML_NODEFDTD
solo está disponible en Libxml 2.7.8 y LIBXML_HTML_NOIMPLIED
está disponible en Libxml 2.7.7)
Un buen truco es usar loadXML
y luego saveHTML
. Las etiquetas html
y body
se insertan en la etapa de load
, no en la etapa de save
.
$dom = new DOMDocument;
$dom->loadXML(''<p>My DOMDocument contents are here</p>'');
echo $dom->saveHTML();
NB que esto es un poco raro y deberías usar la respuesta de Jonás si puedes hacer que funcione.
Use saveXML()
y pase el elemento documentElement como argumento.
$innerHTML = '''';
foreach ($document->getElementsByTagName(''p'')->item(0)->childNodes as $child) {
$innerHTML .= $document->saveXML($child);
}
echo $innerHTML;
Use esta función
$layout = preg_replace(''~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>/s*~i'', '''', $layout);
mi servidor tiene php 5.3 y no se puede actualizar, por lo que esas opciones
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
no son para mi
Para resolver esto le digo a SaveXML Function que imprima el elemento Body y luego simplemente reemplaza el "cuerpo" con "div"
aquí está mi código, espero que esté ayudando a alguien:
<?
$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)));
}
?>
el utf-8 es para el apoyo hebreo.
use DOMDocumentFragment
$html = ''what you want'';
$doc = new DomDocument();
$fragment = $doc->createDocumentFragment();
$fragment->appendXML($html);
$doc->appendChild($fragment);
echo $doc->saveHTML();
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>''
se convierte en:
<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.
This library makes it simple to traverse / modify the DOM and also takes care of removing the doctype / html wrappers for you: