Как сохранитьHTML DOMDocument без оболочки HTML?

Я являюсь функцией ниже, я изо всех сил пытаюсь вывести DOMDocument без добавления в оболочку XML, HTML, body и p тегов перед выходом содержимого. Предлагаемое исправление:

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

Работает только тогда, когда содержимое не содержит элементов уровня блока. Однако, когда это происходит, как в примере ниже с элементом h1, полученный результат saveXML усекается до …

<p> Если вам нравится </ p>

Я был указан на этом посту как возможное обходное решение, но я не могу понять, как его реализовать в этом решении (см. Ниже прокомментированные попытки).

Какие-либо предложения?

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

Все эти ответы теперь ошибочны , поскольку с PHP 5.4 и Libxml 2.6 loadHTML теперь имеет $option который инструктирует Libxml о том, как он должен анализировать содержимое.

Поэтому, если мы загрузим HTML с этими параметрами

 $html->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); 

при выполнении saveHTML() не будет никакого типа doctype , no <html> и no <body> .

LIBXML_HTML_NOIMPLIED отключает автоматическое добавление LIBXML_HTML_NODEFDTD элементов html / body. LIBXML_HTML_NODEFDTD предотвращает добавление LIBXML_HTML_NODEFDTD по умолчанию, если он не найден.

Полная документация о параметрах Libxml приведена здесь

(Обратите внимание, что loadHTML docs говорят, что Libxml 2.6 необходим, но LIBXML_HTML_NODEFDTD доступен только в Libxml 2.7.8, а LIBXML_HTML_NOIMPLIED доступен в Libxml 2.7.7)

Просто удалите узлы сразу после загрузки документа с помощью loadHTML ():

 # remove <!DOCTYPE $doc->removeChild($doc->doctype); # remove <html><body></body></html> $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); 

saveXML() этого используйте saveXML() и передайте documentElement в качестве аргумента.

 $innerHTML = ''; foreach ($document->getElementsByTagName('p')->item(0)->childNodes as $child) { $innerHTML .= $document->saveXML($child); } echo $innerHTML; 

http://php.net/domdocument.savexml

использовать DOMDocumentFragment

 $html = 'what you want'; $doc = new DomDocument(); $fragment = $doc->createDocumentFragment(); $fragment->appendXML($html); $doc->appendChild($fragment); echo $doc->saveHTML(); 

loadXML трюком является использование loadXML а затем saveHTML . Теги html и body вставляются на этапе load , а не в этап save .

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

Обратите внимание, что это немного хаки, и вы должны использовать ответ Джона, если вы можете заставить его работать.

Я немного опаздываю в клуб, но не хочу делиться методом, о котором я узнал. Прежде всего, у меня есть правильные версии для loadHTML (), чтобы принять эти хорошие параметры, но LIBXML_HTML_NOIMPLIED не работал в моей системе. Также пользователи сообщают о проблемах с парсером (например, здесь и здесь ).

Решение, которое я создал на самом деле, довольно просто.

Загружаемый HTML помещается в элемент <div> поэтому у него есть контейнер, содержащий все загружаемые узлы.

Затем этот контейнерный элемент удаляется из документа (но DOMElement из него все еще существует).

Затем удаляются все прямые дети из документа. Это включает в себя любые добавленные теги <html> , <head> и <body> (фактически параметр LIBXML_HTML_NOIMPLIED ), а также объявление <!DOCTYPE html ... loose.dtd"> (фактически LIBXML_HTML_NODEFDTD ).

Затем все прямые дочерние элементы контейнера снова добавляются в документ и могут выводиться.

 $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 работает как обычно, просто позаботьтесь о том, что теперь есть несколько элементов документа, поэтому не один корневой узел:

 $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 ~ точное + 2 (cli) (построено: 21 декабря 2014 20:28:53)

Используйте эту функцию

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

Ни одно из других решений на момент написания этой статьи (июнь 2012 г.) не смогло полностью удовлетворить мои потребности, поэтому я написал тот, который обрабатывает следующие случаи:

  • Принимает текстовое содержимое без тегов, а также содержимое HTML.
  • Не добавляет теги (включая теги <doctype> , <xml> , <html> , <body> и <p> )
  • Оставляет все, что завернуто в <p> .
  • Оставляет пустой текст в покое.

Итак, вот решение, которое устраняет эти проблемы:

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

Я также написал несколько тестов, которые будут жить в том же классе:

 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 } 

Вы можете проверить, что это работает для вас самих. DomDocumentWorkaround::testAll() возвращает это:

  Succeeded Succeeded Succeeded Succeeded 

Хорошо, я нашел более элегантное решение, но это просто утомительно:

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

Хорошо, надеюсь, это ничего не опускает и помогает кому-то?

Проблема с верхним ответом заключается в том, что LIBXML_HTML_NOIMPLIED нестабилен .

Он может изменять порядок элементов (в частности, перемещать закрывающий тег верхнего элемента в нижней части документа), добавлять случайные пэги и, возможно, целый ряд других проблем [1] . Он может удалить теги html и body для вас, но ценой нестабильного поведения. В производстве это красный флаг. Вкратце:

Не используйте LIBXML_HTML_NOIMPLIED . Вместо этого используйте substr .


Думаю об этом. Длины <html><body> и </body></html> фиксированы и на обоих концах документа – их размеры никогда не меняются, а также не занимают свои позиции. Это позволяет нам использовать substr для их отсечения:

 $dom = new domDocument; $dom->loadHTML($html, LIBXML_HTML_NODEFDTD); echo substr($dom->saveHTML(), 12, -15); // the star of this operation 

( ЭТО НЕ ЗАКЛЮЧИТЕЛЬНОЕ РЕШЕНИЕ ОДНАКО! См. Ниже полный ответ , продолжайте читать для контекста)

Мы отделим 12 от начала документа, потому что <html><body> = 12 символов ( <<>>+html+body = 4 + 4 + 4), и мы возвращаемся назад и вырезаем 15 с конца, потому что \n</body></html> = 15 символов ( \n+//+<<>>+body+html = 1 + 2 + 4 + 4 + 4)

Обратите внимание, что я все еще использую LIBXML_HTML_NODEFDTD исключить включение !DOCTYPE . Во-первых, это упрощает удаление субтитров тегов HTML / BODY. Во-вторых, мы не удаляем doctype с помощью substr потому что мы не знаем, будет ли « default doctype » всегда иметь фиксированную длину. Но, что наиболее важно, LIBXML_HTML_NODEFDTD останавливает анализатор DOM от применения к документу типа не HTML5, что, по крайней мере, мешает парсеру обрабатывать элементы, которые он не распознает как свободный текст.

Мы знаем, что теги HTML / BODY имеют фиксированные длины и позиции, и мы знаем, что константы, такие как LIBXML_HTML_NODEFDTD , никогда не удаляются без какого-либо уведомления об устаревании, поэтому вышеупомянутый метод должен хорошо катиться в будущее, НО


… единственное предостережение заключается в том, что реализация DOM может изменить способ размещения тегов HTML / BODY внутри документа – например, удаление новой строки в конце документа, добавление пробелов между тегами или добавление новых строк.

Это можно исправить, выполнив поиск позиций открывающих и закрывающих тегов для body и используя эти смещения, чтобы наши длины были обрезаны. Мы используем strpos и strrpos для поиска смещений спереди и сзади соответственно:

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

В заключение повторим окончательный, будущий ответ :

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

Нет doctype, ни тега html, ни тега тега. Мы можем только надеяться, что парсер DOM скоро получит свежий слой краски, и мы сможем более прямо устранить эти нежелательные метки.

Добавление <meta> приведет к исправлению поведения DOMDocument . Хорошая часть заключается в том, что вам не нужно добавлять этот тег вообще. Если вы не хотите использовать кодировку по своему выбору, просто передайте ее как аргумент конструктора.

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

Вывод

 <div>Hello World</div> 

Благодаря @Bart

У меня тоже было это требование, и мне понравилось решение, опубликованное Алексом выше. Однако есть несколько проблем: если элемент <body> содержит более одного дочернего элемента, результирующий документ будет содержать только первый дочерний элемент <body> , но не все из них. Кроме того, мне нужно было зачистки для обработки вещей условно – только когда у вас был документ с заголовками HTML. Поэтому я уточнил это следующим образом. Вместо удаления <body> я преобразовал его в <div> и удалил объявление XML и <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); } } 

Как и другие участники, я впервые наслаждался простотой и потрясающей силой ответа @Alessandro Vendruscolo. Возможность просто передать некоторые помеченные константы конструктору казалась слишком хорошей, чтобы быть правдой. Для меня это было. У меня есть правильные версии как LibXML, так и PHP, но независимо от того, что еще добавит тег HTML в структуру узла объекта Document.

Мое решение работало лучше, чем использование …

 $html->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); 

Флаги или ….

 # remove <!DOCTYPE $doc->removeChild($doc->firstChild); # remove <html><body></body></html> $doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); 

Удаление узла, который становится беспорядочным без структурированного порядка в DOM. Снова фрагменты кода не имеют возможности предопределять структуру DOM.

Я начал это путешествие, желая простого способа сделать DOM-обход, как это делает JQuery или, по крайней мере, в некотором роде, у которого был структурированный набор данных, либо связанный, дважды связанный, либо обход дерева. Меня не волновало, насколько я могу анализировать строку так, как это делает HTML, а также обладает потрясающей способностью свойств класса сущностей узла.

До сих пор объект DOMDocument оставил меня в желании … Как и многие другие программисты, кажется … Я знаю, что я видел много разочарования в этом вопросе, так как я НАКОНЕЦ … (примерно через 30 часов попытки и неудача типа). Я нашел способ получить все это. Я надеюсь, что это помогает кому-то…

Во-первых, я цинично ВСЕ … lol …

Я бы потратил всю жизнь, прежде чем согласиться с кем-либо, что в любом случае в этом случае используется сторонний класс. Я очень был и не являюсь поклонником использования какой-либо структуры сторонних классов, однако я наткнулся на отличный парсер. (около 30 раз в Google, прежде чем я сдался, так что не чувствуйте себя в одиночестве, если вы избегаете этого, потому что это выглядело каким-то неофициальным …)

Если вы используете фрагменты кода и нуждаетесь в этом, код должен быть очищен и не подвержен влиянию парсера каким-либо образом, без использования дополнительных тегов, а затем используйте simplePHPParser .

Это потрясающе и очень похоже на JQuery. Я не очень впечатлен, но этот класс использует много хороших инструментов, и на данный момент у меня не было ошибок синтаксического анализа. Я большой поклонник того, что могу делать то, что делает этот класс.

Здесь вы можете найти его файлы для загрузки, инструкции по запуску здесь и свой API здесь . Я настоятельно рекомендую использовать этот класс с помощью своих простых методов, которые могут использовать .find(".className") таким же способом, как и метод поиска JQuery, или даже знакомые методы, такие как getElementByTagName() или getElementById()

Когда вы сохраняете дерево узлов в этом классе, оно ничего не добавляет. Вы можете просто сказать $doc->save(); и он выводит все дерево на строку без всякой суеты.

Теперь я буду использовать этот парсер для всех, без ограничений пропускной способности, проектов в будущем.

У меня PHP 5.3, и ответы здесь не сработали для меня.

$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild); заменил весь документ только первым ребенком, у меня было много абзацев, и только первый был сохранен, но решение дало мне хорошую отправную точку для написания чего-то без regex Я оставил некоторые комментарии, и я уверен, что это можно улучшить, но если у кого-то есть такая же проблема, как у меня, это может быть хорошей отправной точкой.

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

Тогда мы могли бы использовать его так:

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

Обратите внимание, что appendChild принимает DOMNode поэтому нам не нужно создавать новые элементы, мы можем просто повторно использовать существующие, которые реализуют DOMNode такие как DOMElement это может быть важно для поддержания « DOMElement кода при манипулировании несколькими документами HTML / XML

Я наткнулся на эту тему, чтобы найти способ удалить оболочку HTML. Использование LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD работает, но у меня проблема с utf-8. После долгих усилий я нашел решение. Я публикую его ниже, у кого есть такая же проблема.

Проблема вызвана тем, что <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Проблема:

 $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(); 

Решение 1:

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

Решение 2:

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

Это 2017 год, и для этого вопроса 2011 года мне не нравятся какие-либо ответы. Множество регулярных выражений, больших классов, loadXML и т. Д. …

Простое решение, которое решает известные проблемы:

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

Легко, просто, твердо, быстро. Этот код будет работать в отношении HTML-тегов и кодировки:

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

Если кто-нибудь найдет ошибку, скажите, я буду использовать это сам.

Изменить , Другие допустимые параметры, которые работают без ошибок (очень похожие на уже заданные):

 @$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 ); 

Вы могли бы добавить тело самостоятельно, чтобы не допустить каких-либо странных вещей на меха.

Вариант майки:

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

Если решение флагов, на которое отвечает Alessandro Vendruscolo , не работает, вы можете попробовать следующее:

 $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 будет содержать полный обработанный HTML-код без всех этих оберток HTML, за исключением <body> , который является корнем вашего содержимого. Затем вы можете использовать функцию регулярного выражения или триммера, чтобы удалить ее из окончательной строки (после saveHTML ) или, как и в случае выше, перебрать все ее дочерние элементы, сохранив их содержимое во временную переменную $finalHtml и $finalHtml ее (что я считаю, что это безопаснее).

Я тоже столкнулся с этой проблемой.

К сожалению, я не чувствовал себя комфортно, используя любое из решений, представленных в этой теме, поэтому я пошел проверить, что удовлетворит меня.

Вот что я составил, и он работает без проблем:

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

В сущности он работает аналогично большинству решений, представленных здесь, но вместо того, чтобы выполнять ручной труд, он использует селектор xpath для выбора всех элементов в теле и конкатенации их html-кода.

мой сервер получил php 5.3 и не может обновиться, поэтому эти параметры

 LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD 

не для меня.

Чтобы решить эту проблему, я скажу функции SaveXML, чтобы напечатать элемент Body, а затем просто замените «body» на «div»,

вот мой код, надеюсь, что он помогает кому-то:

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

utf-8 предназначен для поддержки иврита.

Ответ Alex правильный, но может привести к следующей ошибке на пустых узлах:

Аргумент 1 передан в DOMNode :: removeChild () должен быть экземпляром DOMNode

Вот мой маленький мод:

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

Добавление trim () также является хорошей идеей для удаления пробелов.

Я, может быть, слишком поздно. Но, возможно, кто-то (как и я) все еще имеет эту проблему.
Итак, ни одно из вышеперечисленных не работало для меня. Поскольку $ dom-> loadHTML также закрывает открытые теги, не только добавляйте теги html и body.
Поэтому добавьте элемент <div> для меня не работает, потому что иногда мне иногда нравится 3-4 незакрытых div в html-файле.
Мое решение:

1.) Добавьте маркер для вырезания, затем загрузите элемент html

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

2.) делать все, что захотите, с документом
3.) сохранить html

 $new_html_piece = $dom->saveHTML(); 

4.) перед тем, как вы вернете его, удалите теги <p> </ p> из маркера, как ни странно, он появляется только на [MARK], но не на [/ MARK] …!?

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

5.) удалить все до и после маркера

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

6.) вернуть его

 return $new_html_piece; 

Было бы намного легче, если бы LIBXML_HTML_NOIMPLIED работал для меня. Это может быть, но это не так. PHP 5.4.17, libxml Версия 2.7.8.
Я нахожу действительно странным, я использую парсер HTML DOM, а затем, чтобы исправить эту «вещь», я должен использовать регулярное выражение … Весь смысл заключался в том, чтобы не использовать регулярное выражение;)

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