PHPのTidy拡張でHTMLを処理するとき、「ドキュメント全体ではなく <body> タグの中身だけが欲しい」という場面はよくあります。そのために使うのが tidy_get_body() 関数です。
この関数は整形済みのHTMLから <body> 要素に対応する tidyNode オブジェクト を返します。文字列ではなくノードオブジェクトとして返ってくる点が特徴で、DOMツリーをたどる処理や、body 部分のみのHTML文字列の抽出に活用できます。この記事では基本仕様から実践的な活用パターンまで詳しく解説します。
関数概要
| 項目 | 内容 |
|---|---|
| 関数名 | tidy_get_body() |
| 読み方 | タイディ・ゲット・ボディ |
| 分類 | Tidy関数 |
| 対応バージョン | PHP 5以降(Tidy拡張が有効な場合) |
| 引数 | tidyオブジェクト |
| 戻り値 | tidyNode(body要素のノード) |
| 必要な拡張 | tidy(多くの環境で標準バンドル) |
| OOPスタイル | $tidy->body() |
構文
// 手続き型
tidy_get_body(tidy $tidy): tidyNode
// オブジェクト指向型(推奨)
$tidy->body(): tidyNode
返却される tidyNode オブジェクトは <body> 要素そのものを表すノードで、次のような主要プロパティを持ちます。
| プロパティ | 型 | 内容 |
|---|---|---|
$node->name | string | ノード名("body" など) |
$node->value | string | ノードの内部HTML文字列 |
$node->type | int | ノードの種類(TIDY_NODETYPE_* 定数) |
$node->child | array|null | 子ノードの配列 |
$node->attribute | array|null | 属性の連想配列 |
$node->isHTML() | bool | HTMLノードかどうか |
$node->hasChildren() | bool | 子ノードを持つかどうか |
show-body-only 設定との違い
<body> 部分だけのHTML文字列が欲しい場合、設定オプション show-body-only => true を使う方法もあります。両者の使い分けを整理します。
【show-body-only => true を設定した場合】
(string) $tidy → "<body> タグを含まない body の中身の文字列" を返す
【tidy_get_body() を使う場合】
$node = tidy_get_body($tidy)
$node->value → "<body> タグを含む body 全体の文字列" を返す
$node->child → 子ノードのリストとして操作できる
「ただ <body> の中身を文字列として取り出したい」だけなら show-body-only が手軽です。DOMツリーとして操作・探索したい場合は tidy_get_body() が適しています。
処理の流れ
tidy_parse_string() / tidy_parse_file()
│
▼
tidy_clean_repair()
│
▼
tidy_get_body()
│
▼
tidyNode(body要素)
│
├─ $node->value → body の HTML 文字列
├─ $node->child → 子ノードの配列
├─ $node->name → "body"
└─ $node->attribute → id, class などの属性
基本的な使い方
<?php
$html = '<html><head><title>テスト</title></head><body><h1>見出し</h1><p>本文</p></body></html>';
$tidy = tidy_parse_string($html, [], 'UTF8');
tidy_clean_repair($tidy);
$body = tidy_get_body($tidy);
echo "ノード名: " . $body->name . PHP_EOL;
echo "---" . PHP_EOL;
echo $body->value . PHP_EOL;
実行結果:
ノード名: body
---
<body>
<h1>見出し</h1>
<p>本文</p>
</body>
$body->value には <body> タグ自体を含む形でHTML文字列が入ります。<body> タグを除いた中身だけが必要なら、後述の正規表現や show-body-only 設定を使います。
実践的なコード例
例1:body の中身だけをHTML文字列として取り出す
<?php
class BodyExtractor
{
public function extract(string $fullHtml): string
{
$tidy = tidy_parse_string($fullHtml, [
'output-xhtml' => true,
'wrap' => 0,
], 'UTF8');
tidy_clean_repair($tidy);
$bodyNode = tidy_get_body($tidy);
// $bodyNode->value は <body>...</body> を含むので
// 最外周の <body> タグを除去して中身だけを返す
$html = $bodyNode->value;
$html = preg_replace('/^<body[^>]*>/i', '', $html);
$html = preg_replace('/<\/body>$/i', '', $html);
return trim($html);
}
}
$extractor = new BodyExtractor();
$full = <<<HTML
<!DOCTYPE html>
<html>
<head><title>ページタイトル</title></head>
<body>
<h1>見出し</h1>
<p>本文テキスト</p>
</body>
</html>
HTML;
echo $extractor->extract($full);
実行結果:
<h1>見出し</h1>
<p>本文テキスト</p>
例2:子ノードを再帰的に走査してテキストノードを収集する
<?php
class TextNodeCollector
{
private array $texts = [];
public function collect(string $html): array
{
$tidy = tidy_parse_string($html, [], 'UTF8');
tidy_clean_repair($tidy);
$this->texts = [];
$this->traverse(tidy_get_body($tidy));
return $this->texts;
}
private function traverse(tidyNode $node): void
{
// テキストノード(TIDY_NODETYPE_TEXT = 3)を収集
if ($node->type === TIDY_NODETYPE_TEXT) {
$text = trim($node->value);
if ($text !== '') {
$this->texts[] = $text;
}
}
if ($node->hasChildren()) {
foreach ($node->child as $child) {
$this->traverse($child);
}
}
}
}
$collector = new TextNodeCollector();
$texts = $collector->collect(
'<html><body><h1>タイトル</h1><p>段落1</p><p>段落2</p></body></html>'
);
print_r($texts);
実行結果:
Array
(
[0] => タイトル
[1] => 段落1
[2] => 段落2
)
tidy_get_body() で取得した tidyNode を起点に child をたどることで、DOMツリーを再帰的に走査してテキストコンテンツだけを抽出できます。
例3:body直下の要素名一覧を取得する
<?php
class DirectChildTagLister
{
public function list(string $html): array
{
$tidy = tidy_parse_string($html, [], 'UTF8');
tidy_clean_repair($tidy);
$body = tidy_get_body($tidy);
$tags = [];
if ($body->hasChildren()) {
foreach ($body->child as $child) {
// HTMLノードのみ(テキストノードを除外)
if ($child->type === TIDY_NODETYPE_START || $child->type === TIDY_NODETYPE_STARTEND) {
$tags[] = $child->name;
}
}
}
return $tags;
}
}
$lister = new DirectChildTagLister();
$tags = $lister->list(
'<html><body><header>ヘッダー</header><main><p>本文</p></main><footer>フッター</footer></body></html>'
);
print_r($tags);
実行結果:
Array
(
[0] => header
[1] => main
[2] => footer
)
例4:OOPスタイル(body())での記述
<?php
class OopBodyNodeDemo
{
private tidy $tidy;
public function parse(string $html): self
{
$this->tidy = new tidy();
$this->tidy->parseString($html, [
'output-xhtml' => true,
'wrap' => 0,
], 'UTF8');
$this->tidy->cleanRepair();
return $this;
}
public function getBodyInfo(): array
{
// OOPスタイルの body() は tidy_get_body() と同等
$body = $this->tidy->body();
return [
'name' => $body->name,
'has_children' => $body->hasChildren(),
'child_count' => $body->hasChildren() ? count($body->child) : 0,
];
}
}
$demo = new OopBodyNodeDemo();
$info = $demo->parse('<html><body><p>A</p><p>B</p><p>C</p></body></html>')
->getBodyInfo();
print_r($info);
実行結果:
Array
(
[name] => body
[has_children] => 1
[child_count] => 3
)
例5:body の属性(class・id)を取得する
<?php
class BodyAttributeReader
{
public function read(string $html): array
{
$tidy = tidy_parse_string($html, [], 'UTF8');
tidy_clean_repair($tidy);
$body = tidy_get_body($tidy);
$attributes = $body->attribute ?? [];
return $attributes;
}
}
$reader = new BodyAttributeReader();
$attrs = $reader->read(
'<html><body class="home dark-mode" id="main-body"><p>本文</p></body></html>'
);
print_r($attrs);
実行結果:
Array
(
[class] => home dark-mode
[id] => main-body
)
$body->attribute は属性名をキー、属性値を値とする連想配列で、<body> タグに付いた class や id などを取得できます。
例6:特定のタグを body ノードから検索する
<?php
class TagFinder
{
private array $found = [];
public function find(string $html, string $tagName): array
{
$tidy = tidy_parse_string($html, [], 'UTF8');
tidy_clean_repair($tidy);
$this->found = [];
$this->search(tidy_get_body($tidy), strtolower($tagName));
return $this->found;
}
private function search(tidyNode $node, string $tag): void
{
if (strtolower($node->name ?? '') === $tag) {
$this->found[] = $node->value;
}
if ($node->hasChildren()) {
foreach ($node->child as $child) {
$this->search($child, $tag);
}
}
}
}
$finder = new TagFinder();
$links = $finder->find(
'<html><body><p><a href="/top">トップ</a></p><p><a href="/about">会社概要</a></p></body></html>',
'a'
);
foreach ($links as $link) {
echo $link . PHP_EOL;
}
実行結果:
<a href="/top">トップ</a>
<a href="/about">会社概要</a>
例7:body ノードの存在チェックつき安全な呼び出し
<?php
class SafeBodyNodeAccessor
{
public function getBodyValue(string $html): ?string
{
if (!extension_loaded('tidy')) {
trigger_error('tidy拡張が利用できません。', E_USER_WARNING);
return null;
}
$tidy = tidy_parse_string($html, ['wrap' => 0], 'UTF8');
tidy_clean_repair($tidy);
$body = tidy_get_body($tidy);
if (!isset($body->value)) {
return null;
}
return $body->value;
}
}
$accessor = new SafeBodyNodeAccessor();
$value = $accessor->getBodyValue('<html><body><p>安全なアクセス</p></body></html>');
echo $value ?? '取得できませんでした';
実行結果:
<body>
<p>安全なアクセス</p>
</body>
関連関数との比較
| 関数 | OOPスタイル | 返却する要素 |
|---|---|---|
tidy_get_body() | body() | <body> 要素の tidyNode |
tidy_get_head() | head() | <head> 要素の tidyNode |
tidy_get_html() | html() | <html> 要素の tidyNode |
tidy_get_root() | root() | ドキュメントルートの tidyNode |
tidy_get_output() | value プロパティ | ドキュメント全体のHTML文字列 |
show-body-only 設定 | — | <body> 内の中身だけのHTML文字列(タグなし) |
よくある落とし穴・注意点
$node->valueには<body>タグ自体が含まれるtidy_get_body()が返すtidyNodeのvalueプロパティは<body>...</body>全体を含みます。<body>タグを除いた中身だけが欲しい場合は、show-body-only => true設定を使うか、正規表現で外周タグを除去する処理を加えてください。tidy_clean_repair()を呼ばないとノードが不完全tidy_get_body()はcleanRepair()実行後のHTMLに基づいてノードを構築します。cleanRepair()を省略すると、修正前の不完全なHTMLに基づくノードが返される場合があります。必ずparse → cleanRepair → get_bodyの順で呼び出してください。$node->childがnullになる場合がある<body>要素が空、またはテキストノードのみで構成されている場合、$node->childがnullになることがあります。$node->hasChildren()で子ノードの存在を確認してからアクセスしましょう。- フラグメント(body タグなし)を渡すと Tidy が補完する
<body>タグのないHTMLフラグメントをtidy_parse_string()に渡しても、Tidyが自動で<body>を補完するため、tidy_get_body()は常に有効なノードを返します。ただし補完されたHTMLが意図通りかどうかはerrorBufferで確認しましょう。 tidyNodeはDOMNodeとは別物 PHP のDOMDocumentクラスが扱うDOMNodeとは異なるオブジェクトです。Tidy のtidyNodeを DOMDocument のメソッドで操作しようとするとエラーになります。DOMDocument での高度な操作が必要な場合は、tidy_get_body()で取得したHTML文字列をいったんDOMDocument::loadHTML()に渡して変換する方法を検討してください。
まとめ
| ポイント | 内容 |
|---|---|
| 役割 | HTMLの <body> 要素を tidyNode オブジェクトとして取得する |
| 引数 | tidyオブジェクト |
| 戻り値 | tidyNode(body要素のノード) |
| OOPスタイル | $tidy->body() |
$node->value | <body> タグを含む body 全体のHTML文字列 |
| よく組み合わせる関数・プロパティ | tidy_clean_repair(), $node->child, $node->hasChildren() |
show-body-only との違い | ノード操作が可能か(tidy_get_body)vs 文字列だけ欲しい(show-body-only) |
| 主な用途 | body部分の抽出、DOMツリー走査、テキスト・タグの収集、属性の取得 |
| 注意点 | value に <body> タグが含まれる、child は null チェックが必要 |
tidy_get_body() は「HTMLの <body> 部分をDOMノードとして扱いたい」場面で力を発揮します。単純な文字列抽出なら show-body-only 設定のほうが手軽ですが、子ノードの走査・属性の取得・タグの検索など、ツリー構造を活かした処理が必要なときは tidy_get_body() が適しています。
