web-dev-qa-db-ja.com

HTMLラッパーなしでDOMDocumentのHTMLを保存する方法は?

私は以下の関数です。コンテンツの出力の前にXML、HTML、bodyおよびpタグラッパーを追加せずにDOMDocumentを出力するのに苦労しています。推奨される修正:

$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;
}
101
Scott B

これらの答えはすべてwrongです。なぜならPHP 5.4およびLibxml 2.6では loadHTML が_$option_パラメーターは、Libxmlにコンテンツの解析方法を指示します。

したがって、これらのオプションでHTMLをロードすると

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

saveHTML()を実行すると、doctype、_<html>_、_<body>_がなくなります。

_LIBXML_HTML_NOIMPLIED_は、暗黙のhtml/body要素の自動追加をオフにします_LIBXML_HTML_NODEFDTD_は、見つからない場合にデフォルトのdoctypeが追加されるのを防ぎます。

Libxmlパラメータに関する完全なドキュメントは here です

loadHTMLのドキュメントではLibxml 2.6が必要であると書かれていますが、_LIBXML_HTML_NODEFDTD_はLibxml 2.7.8でのみ利用可能で、_LIBXML_HTML_NOIMPLIED_はLibxml 2.7.7で利用可能です)

192

LoadHTML()でドキュメントをロードした後、ノードを直接削除します。

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

# remove <html><body></body></html> 
$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);
66
Alex

代わりにsaveXML()を使用し、documentElementを引数として渡します。

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

http://php.net/domdocument.savexml

18
Jonah

dOMDocumentFragmentを使用する

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

巧妙なトリックは、 loadXML を使用してから saveHTML を使用することです。 htmlおよびbodyタグは、loadステージではなく、saveステージに挿入されます。

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

注意してください、これは少しハックであり、それを動作させることができる場合は、ジョナの答えを使用する必要があります。

13
lonesomeday

一番の答えの問題は、_LIBXML_HTML_NOIMPLIED_が不安定であることです。

要素を並べ替えることができ(特に、上部要素の終了タグをドキュメントの下部に移動します)、ランダムなpタグを追加し、おそらく他のさまざまな問題を追加できます [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
_

THIS IS NOT THE FINAL SOLUTION HOWEVER!ただし、完全な答えについては以下を参照してください、コンテキストを読んでください)

_12_ = 12文字(_<html><body>_ = 4 + 4 + 4)であるため、ドキュメントの先頭から_<<>>+html+body_を切り取り、_\n</body></html>_ = 15文字(_\n+//+<<>>+body+html_ = 1 + 2 + 4 + 4 + 4)

私はまだ_LIBXML_HTML_NODEFDTD_を使用していることに注意してください。_!DOCTYPE_は含まれません。まず、これにより、HTML/BODYタグのsubstr削除が簡単になります。次に、 ' _default doctype_ 'が常に固定長であるかどうかわからないため、substrでdoctypeを削除しません。しかし、最も重要なことは、_LIBXML_HTML_NODEFDTD_は、DOMパーサーがHTML5以外のdoctypeをドキュメントに適用するのを防ぎます-これにより、パーサーが少なくともルーズテキストとして認識しない要素を処理できなくなります。

HTML/BODYタグの長さと位置が固定されているという事実を知っています。また、_LIBXML_HTML_NODEFDTD_のような定数は、何らかの非推奨通知なしに削除されることはありません。したがって、上記のメソッドは、 [〜#〜] but [〜#〜]...


...唯一の注意点は、DOM実装could HTML/BODYタグがドキュメント内に配置される方法を変更することです。たとえば、ドキュメントの最後の改行を削除し、タグ、または改行の追加。

これは、bodyの開始タグと終了タグの位置を検索し、長さを調整するためにそれらのオフセットを使用して修正できます。 strposstrrpos を使用して、それぞれ前面と背面からのオフセットを見つけます。

_$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タグ、bodyタグはありません。 DOMパーサーがペイントの新しいコートをすぐに受け取ることを望み、これらの不要なタグをより直接的に排除できることを願っています。

12
Super Cat

私はクラブに少し遅れていますが、私が見つけた方法をnot共有したくありませんでした。まず、これらのNiceオプションを受け入れるloadHTML()の適切なバージョンがありますが、LIBXML_HTML_NOIMPLIED私のシステムでは動作しませんでした。また、ユーザーはパーサーの問題を報告します(たとえば、 here および here )。

実際に作成したソリューションは非常に簡単です。

ロードされる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は通常どおり動作します。ただ1つのルートノードではなく、複数のドキュメント要素があることに注意してください。

$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)
10
hakre

それは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());
10
Vixxs

さて、よりエレガントなソリューションを見つけましたが、それは退屈なだけです。

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

さて、うまくいけば、これは何も省略せず、誰かを助けますか?

4
rclai

この機能を使用

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

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_には、コンテンツのルートである_<body>_タグを除き、これらすべてのHTMLラップなしで完全に処理されたHTMLコードが含まれます。次に、正規表現またはトリム関数を使用して、(saveHTMLの後の)最終文字列から削除するか、上記の場合のように、すべての子を繰り返して、その内容を一時変数_$finalHtml_そしてそれを返します(私はより安全だと信じています)。

この記事の執筆時点(2012年6月)で他のソリューションはどれも私のニーズを完全に満たすことができなかったため、次のケースを処理するソリューションを作成しました。

  • タグのないプレーンテキストコンテンツと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
_
3
plowman

<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 に感謝

2
botenvouwer

私にもこの要件があり、上記のアレックスが投稿したソリューションが気に入りました。ただし、いくつかの問題があります-<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);
    }
}
2
blackcatweb

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

appendChildDOMNode を受け入れるため、新しい要素を作成する必要はなく、 DOMNode のようなDOMElementを実装する既存の要素を再利用できます。複数のHTML/XMLドキュメントを操作するときは、コードを「正常」に保つことが重要です

2

他のメンバーと同じように、私は最初に@Alessandro Vendruscoloの答えのシンプルさと素晴らしいパワーに驚いた。いくつかのフラグ付き定数をコンストラクターに単純に渡す機能は、あまりにも優れているように思えました。私にとってはそうでした。 LibXMLとPHPの両方の正しいバージョンがありますが、それでも、ドキュメントオブジェクトのノード構造にHTMLタグが追加される場合は関係ありません。

私のソリューションは...

$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構造を事前に決定する方法がありません。

私は、JQueryの方法でDOMトラバーサルを行う簡単な方法、または単一リンク、二重リンク、またはツリー化されたノードトラバーサルの構造化データセットを持つ少なくとも何らかの方法でこの旅を始めました。 HTMLのように文字列を解析できる限り気にしませんでしたし、ノードエンティティクラスプロパティの驚くべき力を途中で使用することもできました。

これまでのところ、DOMDocument Objectは私を欲しがっていません...他の多くのプログラマーと同様に...この質問に多くのフラストレーションを感じているので、私はついに...(約30時間の試行錯誤の後)型テスト)すべてを手に入れる方法を見つけました。これが誰かの助けになることを願っています...

最初に、私はすべてのシニカルです...lol ...

このユースケースでサードパーティのクラスが必要であると誰かに同意する前に、私は一生をかけていたでしょう。私はサードパーティのクラス構造を使用するのが大好きでありませんでしたが、素晴らしいパーサーに出くわしました。 (私がやる前にGoogleで約30回行ったので、どうにかして非公式のラメに見えたのでそれを避けたとしても孤独を感じないでください...)

コードフラグメントを使用していて、余分なタグを使用せずに、パーサーの影響を受けないクリーンなコードが必要な場合は、 simplePHPParser を使用します。

それは驚くべきことであり、JQueryによく似ています。私はあまり感心しませんでしたが、このクラスは多くの優れたツールを使用しており、まだ解析エラーはありませんでした。私はこのクラスがやることができるという大ファンです。

ダウンロードするファイルを見つけることができます here 、起動手順 here 、およびAPI here 。 JQueryのfindメソッドと同じ方法で.find(".className")を実行できる単純なメソッド、またはgetElementByTagName()getElementById()などの使い慣れたメソッドでこのクラスを使用することを強くお勧めします。 ..

このクラスでノードツリーを保存すると、何も追加されません。 $doc->save();と言うだけで、ツリー全体が大騒ぎせずに文字列に出力されます。

今後、このパーサーを、帯域幅のないすべてのプロジェクトに使用します。

2
GoreDefex

PHP 5.6.25およびLibXML 2.9を実行しているRHEL7ではこれに苦労しています。

Alessandro Vendruscoloによって提案された大いに支持された解決策は、タグを再配置することでHTMLを壊すことがわかりました。つまり:

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

になる:

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

これは、彼があなたが使用することを提案する両方のオプションに当てはまります:LIBXML_HTML_NOIMPLIEDおよびLIBXML_HTML_NODEFDTD

Alexによって提案された解決策はそれを解決するための半分の方法になりますが、<body>には複数の子ノードがあります。

私のために働く解決策は次のとおりです:

まず、DOMDocumentをロードするには、次を使用します。

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

DOMDocumentをマッサージした後にドキュメントを保存するには、次を使用します。

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

これが非常にエレガントなソリューションではないことに同意する最初の人ですが、動作します。

2
Free Radical

HTMLラッパーを削除する方法を見つけるためにこのトピックに出会いました。 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));
#remove doctype tag
$doc->removeChild($doc->doctype); 

#remove html & body tags
$html = $doc->getElementsByTagName('html')[0];
$body = $html->getElementsByTagName('body')[0];
foreach($body->childNodes as $child) {
    $doc->appendChild($child);
}
$doc->removeChild($html);
0
Dylan Maxey

私もこの問題に出会いました。

残念ながら、このスレッドで提供されているソリューションを快適に使用できなかったため、満足できるものをチェックしに行きました。

これが私が作成したもので、問題なく動作します:

$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コードを連結します。

0

私のサーバーは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はヘブライ語サポート用です。

0
Tomer Ofer

このライブラリにより、DOMのトラバース/変更が簡単になり、doctype/htmlラッパーも削除されます:

https://github.com/sunra/php-simple-html-dom-parser

0
GSTAR

アレックスの答えは正しいですが、空のノードで次のエラーが発生する可能性があります。

DOMNode :: removeChild()に渡される引数1は、DOMNodeのインスタンスでなければなりません

ここに私の小さな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;

空白を削除するには、trim()を追加することもお勧めします。

0
redaxmedia

Drupalを使用しているユーザーには、これを行うための組み込み関数があります。

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

参照用のコード:

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;
  }
}
0
leon.nk

多分遅すぎる。しかし、おそらく私のような誰かがこの問題を抱えています。
だから、上記のどれも私にとってはうまくいきませんでした。 $ dom-> loadHTMLは開始タグも閉じるため、htmlタグとbodyタグを追加するだけではありません。
そのため、<div>要素を追加しても機能しません。htmlピースに3〜4個の閉じられていないdivが含まれることがあるからです。
私の解決策:

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

0
Joe