技術資料

PHP 8.3でHTMLのtableを解析する:DOMDocumentとDOMXPathで一覧データを抽出

作成日:2026.09.05

PHP

PHP 8.3のDOMDocumentとDOMXPathを使い、HTMLファイルに記載された一覧データから、特定の情報を抽出する方法を解説します。DOMElementやDOMNodeの扱い、XPathによるtableの検索、不正なHTMLや日本語文字コードへの注意点に加え、外部URLからHTMLを取得する場合に確認したい利用条件やサーバー負荷についても説明します。

HTMLファイルの中にある一覧表から、必要なデータだけを取り出したい時の方法についてまとめました。

今回は、PHP 8.3のDOMDocumentDOMXPathを使って、全国自然史博物館一覧を記載したサンプル用HTMLファイルを読み込みます。表の行から、博物館名と都道府県・市区町村の住所を抽出して、配列へ変換してみます。

HTMLを文字列として正規表現で処理するのではなく、HTMLの要素をDOMツリーとして扱います。DOMElementDOMNodeの役割や、表の行が見つからない場合の処理も確認します。

前提

今回の動作確認環境は以下の通りです。

  • OS: Windows 11
  • PHP: 8.3.15 CLI
  • ターミナル: PowerShell
  • 使用する拡張: DOM、libxml、mbstring
  • 入力: ローカルに保存したHTMLファイル

PHPのDOM拡張は、HTMLやXMLをノードの階層として扱うための機能です。

なお、PHP 8.3のDOMDocument::loadHTMLFile()は、現在のブラウザーと同じHTML5の解析規則を使うわけではありません。PHP 8.4以降にはHTML5に対応するDom\HTMLDocumentが追加されていますが、この記事ではPHP 8.3のDOMDocumentを対象にします。

今回使うHTMLファイルを用意する

まず、PHPスクリプトと同じディレクトリにsample-museums.htmlを作成します。表には、博物館名と都道府県・市区町村を含む所在地を記載します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>自然史博物館一覧</title>
</head>
<body>
    <h1>全国の自然史博物館</h1>

    <table id="museum-list">
        <thead>
            <tr>
                <th>博物館名</th>
                <th>所在地</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>北海道大学総合博物館</td>
                <td>北海道札幌市</td>
            </tr>
            <tr>
                <td>国立科学博物館</td>
                <td>東京都台東区</td>
            </tr>
            <tr>
                <td>福井県立恐竜博物館</td>
                <td>福井県勝山市</td>
            </tr>
            <tr>
                <td>大阪市立自然史博物館</td>
                <td>大阪府大阪市</td>
            </tr>
            <tr>
                <td>北九州市立いのちのたび博物館</td>
                <td>福岡県北九州市</td>
            </tr>
        </tbody>
    </table>
</body>
</html>

実際のWebページでは、表の列が増えたり、行の中にリンクやアイコンが入ったりすることがあります。今回は処理を分かりやすくするため、1行目のセルを博物館名、2行目のセルを所在地として扱います。

HTMLファイルをDOMDocumentへ読み込む

HTMLファイルを読み込むには、DOMDocumentloadHTMLFile()を使います。戻り値は、読み込みに成功するとtrue、失敗するとfalseです。

不正なHTMLを読み込んだ場合でも、補正しながら読み込みが成功することがあります。そのときに出力されるlibxmlの警告を確認できるよう、内部エラーを一時的に有効にします。

$inputFile = __DIR__ . '/sample-museums.html';

if (!is_file($inputFile) || !is_readable($inputFile)) {
    throw new RuntimeException('HTMLファイルを読み込めません。');
}

$previousUseErrors = libxml_use_internal_errors(true);
libxml_clear_errors();

try {
    $document = new DOMDocument();
    $loaded = $document->loadHTMLFile($inputFile, LIBXML_NONET);
    $parseErrors = libxml_get_errors();
} finally {
    libxml_clear_errors();
    libxml_use_internal_errors($previousUseErrors);
}

if (!$loaded) {
    throw new RuntimeException('HTMLの解析に失敗しました。');
}

foreach ($parseErrors as $parseError) {
    fwrite(STDERR, trim($parseError->message) . PHP_EOL);
}

LIBXML_NONETは、解析時にネットワークへアクセスしないためのオプションです。今回の入力はローカルファイルですが、HTML内に外部リソースへの参照があっても、解析処理からネットワークへ接続しないように指定しています。

libxml_use_internal_errors(true)を使うと、通常のエラー出力へ直接表示せず、libxml_get_errors()でエラー情報を取得できます。処理が終わったらlibxml_clear_errors()でバッファを消し、元の設定へ戻します。

今回のようなサンプルHTMLでは、$parseErrorsは空になります。不正なHTMLで警告が発生しても、警告があることだけを理由に処理を中断するかどうかは、アプリケーションの目的に合わせて決めます。少なくとも、警告の有無とloadHTMLFile()の戻り値は別に扱います。

DOMDocument・DOMElement・DOMNodeの役割

DOMでは、HTML全体や要素をノードのつながりとして扱います。

  • DOMDocument: HTML文書全体を表すオブジェクト
  • DOMElement: tabletrtdなどの要素を表すオブジェクト
  • DOMNode: 要素やテキストノードなど、DOMツリー上のノードを表す基底的なオブジェクト
  • DOMXPath: XPath式を使って、DOMツリーから対象のノードを検索するオブジェクト

要素の表示文字列を取得するときは、DOMNodetextContentを使えます。要素の属性を取得するときは、DOMElementgetAttribute()を使います。

例えば、先ほど読み込んだ文書から、一覧表のid属性を取得する処理は次のようになります。

$tables = $document->getElementsByTagName('table');
$table = $tables->item(0);

if ($table instanceof DOMElement) {
    echo $table->getAttribute('id'), PHP_EOL;
    // museum-list
}

getElementsByTagName()でもタグ名による検索はできますが、複数の表があるページで条件を細かく指定したい場合は、次のDOMXPathを使う方が意図を表しやすくなります。

DOMXPathでtableの行を検索する

DOMXPathを作成し、query()へXPath式を渡します。今回は、idmuseum-listである表のうち、tdを含む行だけを検索します。

$xpath = new DOMXPath($document);
$rows = $xpath->query('//table[@id="museum-list"]//tr[td]');

if ($rows === false) {
    throw new RuntimeException('XPathの評価に失敗しました。');
}

echo $rows->length, PHP_EOL;
// 5

//table[@id="museum-list"]は、id属性が一致するtableを表します。続く//tr[td]は、その表の中から、tdを含むtrを表します。

見出し行はthを使っているため、このXPathの結果には含まれません。また、HTMLの解析時にtbodyが補われることがあっても、//trで検索するため、今回の処理では影響を受けにくくなります。

博物館名と住所を配列へ変換する

検索した各行から、子要素であるtdを取得します。セル内に改行や余分な空白が含まれる場合に備えて、テキストを1つの空白へ正規化する関数も用意します。

function normalizeText(DOMNode $node): string
{
    $text = preg_replace('/\s+/u', ' ', $node->textContent);

    return trim($text ?? '');
}

$museums = [];

foreach ($rows as $row) {
    if (!$row instanceof DOMElement) {
        continue;
    }

    $cells = $xpath->query('./td', $row);

    if ($cells === false || $cells->length < 2) {
        continue;
    }

    $nameNode = $cells->item(0);
    $addressNode = $cells->item(1);

    if (!$nameNode instanceof DOMNode || !$addressNode instanceof DOMNode) {
        continue;
    }

    $name = normalizeText($nameNode);
    $address = normalizeText($addressNode);

    if ($name === '' || $address === '') {
        continue;
    }

    $museums[] = [
        'name' => $name,
        'address' => $address,
    ];
}

query()の第2引数へ$rowを渡すと、文書全体ではなく、その行を基準に./tdを検索できます。./は、現在の行の直下を基準にする指定です。

今回のコードでは、セルが2つ未満の行や、博物館名・住所が空の行は結果へ追加しません。入力データによっては空値を保持したい場合もあるため、スキップするかどうかは用途に合わせて変更してください。

抽出結果を表示する

抽出した配列を表示して、期待した件数と値になっているか確認します。

echo '取得件数: ' . count($museums) . PHP_EOL;

foreach ($museums as $museum) {
    echo $museum['name'] . ' / ' . $museum['address'] . PHP_EOL;
}

今回のサンプルでは、次のような結果になります。

取得件数: 5
北海道大学総合博物館 / 北海道札幌市
国立科学博物館 / 東京都台東区
福井県立恐竜博物館 / 福井県勝山市
大阪市立自然史博物館 / 大阪府大阪市
北九州市立いのちのたび博物館 / 福岡県北九州市

この時点では、HTMLの見た目を再現しているのではなく、DOMツリー上のセルからテキストを取り出しています。取得した配列をJSONに変換したり、データベースへ保存したりする処理は、抽出処理の後ろへ追加できます。

PHPスクリプトを実行する

ここまでの処理をparse-museums.phpへまとめ、sample-museums.htmlと同じディレクトリへ保存します。PowerShellでPHPのバージョンと拡張を確認します。

php -v
php -m | Select-String "dom|libxml|mbstring"

続いて、PHPスクリプトを実行します。

php parse-museums.php

次の内容を確認します。

  • HTMLファイルが読み込まれ、5件の行が検索できる
  • 博物館名と都道府県・市区町村の住所が配列へ入っている
  • 日本語の文字列が文字化けしていない
  • セルが不足した行を誤って結果へ追加していない
  • 対象の表がない場合に、0件として扱うかエラーにするかが決まっている

不正なHTMLと日本語文字コードの注意点

DOMDocument::loadHTMLFile()は、XMLのように完全に整ったHTMLだけを受け付けるわけではありません。不正なマークアップを補正して読み込むことがありますが、補正後のDOM構造が、作成者の想定やブラウザーの表示結果と一致するとは限りません。

そのため、解析対象のHTMLに不備がある場合は、次の内容を分けて確認します。

  • ファイルが存在しない、または読み込めない
  • HTMLの解析自体に失敗した
  • 解析はできたが、対象のtableが存在しない
  • 表は存在するが、対象の行やセルが存在しない
  • 値は取得できたが、文字コードが合わず文字化けしている

日本語を含むHTMLでは、HTMLファイルの保存形式と、HTML内のmeta charset、実際の文字コードが一致しているかを確認します。DOM拡張はUTF-8を前提に動作するため、別の文字コードのHTMLを扱う場合は、読み込み前の変換方法を検討してください。

また、PHP 8.3のDOMDocumentはHTML5準拠のサニタイズ機能ではありません。取得したHTMLをそのまま安全なHTMLとして再出力したり、利用者へ表示したりする用途では、別途サニタイズ処理とセキュリティ検証が必要です。

外部URLからHTMLを取得する場合の注意点

今回はローカルファイルを読み込みました。外部URLからHTMLを取得する処理まで追加すると、HTMLの解析以外に、取得先のサーバーやデータの利用条件も考える必要があります。

取得先の利用条件を確認する

Webページを機械的に取得する前に、利用規約、提供されているAPI、データの利用条件、robots.txtの内容を確認します。robots.txtだけで利用可否を判断せず、取得先が用意している正式なAPIやデータ提供方法があれば、そちらを優先しましょう。

アクセス頻度とキャッシュを考える

短い間隔で同じページを何度も取得すると、相手側サーバーへ負荷をかける可能性があります。取得件数や実行頻度を抑え、必要に応じてキャッシュを使います。定期的に取得する場合は、更新日時やETagなどを利用できるかも確認します。

タイムアウトと取得サイズを制限する

外部サーバーが応答しない場合にPHPの処理が長時間待ち続けないよう、接続と読み込みのタイムアウトを設定します。想定外に大きなレスポンスを保存・解析しないよう、取得サイズの上限も決めます。

User-Agentと連絡先を明示する

取得処理を運用する場合は、どのアプリケーションからのアクセスか分かるUser-Agentを設定し、必要に応じて連絡先を示します。相手側から停止や頻度変更の依頼があった場合に対応できるよう、取得処理の実行元と停止方法も管理しておきます。

利用者が指定したURLを取得する場合

利用者が入力したURLをサーバーから取得する機能では、SSRFにも注意が必要です。内部ネットワークやクラウドのメタデータサービスへ接続されないよう、許可するホストを限定し、リダイレクト先、ポート、取得サイズ、タイムアウトを確認します。

外部URLから取得したHTMLに含まれる文章や画像を、自由に再利用できるとは限りません。著作権、データ提供元の規約、個人情報、表示時の出典なども確認した上で利用します。

まとめ

PHP 8.3でHTMLファイルの一覧表を解析するときは、DOMDocumentへHTMLを読み込み、DOMXPathで対象のtabletrを検索します。各行からDOMNodetextContentを取得し、博物館名と住所の配列へ変換できます。

HTMLが不正な場合は、解析に成功したことと、期待した要素を取得できたことを別々に確認します。日本語を扱う場合は、HTMLの保存形式や文字コードも確認します。

外部URLへ対象を広げる場合は、HTMLの解析方法だけでなく、取得先の利用条件、アクセス頻度、キャッシュ、タイムアウト、取得サイズ、SSRF対策まで含めて設計することが大切です。

参考資料

この記事を書いた人

※上が私です。

奈良市を拠点に、27年以上の経験を持つフリーランスWebエンジニア、阿部辰也です。

これまで、ECサイトのバックエンド開発や業務効率化システム、公共施設の予約システムなど、多彩なプロジェクトを手がけ、企業様や制作会社様のパートナーとして信頼を築いてまいりました。

【制作会社・企業様向けサポート】
  • 専任エンジニアのいない企業様に対するシステム面の不安を解消
  • 柔軟な契約形態や短納期での対応により、急なニーズにも迅速にサポート
  • システムの企画段階から運用まで、ワンストップでのサービスを提供

Webシステムの開発やサイト改善でお困りの際は、どうぞお気軽にご相談ください。小さな疑問から大規模プロジェクトまで、最適なご提案を心を込めてさせていただきます。

ぜひ、プロフィールWeb制作会社様向け業務案内一般企業様向け業務案内もご覧くださいね。

PHPでメールOTPによる追加認証を実装する:発行・保存・送信・検証の設計

2026.09.04

PHP 8.3以上を対象に、パスワードログイン後にメールOTPで追加認証を行う処理を、フレームワークに依存しない形で整理します。コードの生成・保存・送信・検証、期限切れや再送、試行回数制限を扱い、メールOTPをTOTPやパスキーと同等に扱わないための注意点も説明します。

PHP

Laravel 13で既存のテーブルを使ってログイン機能を実装する

2026.09.02

Laravel 13の既存連載で作成したmembersテーブルを認証用ユーザーとして流用し、Memberモデルと認証プロバイダーを設定します。SQLiteに保存したユーザー情報を使ったメールアドレス・パスワード認証、ログイン済み画面の表示、データベースセッション、ログアウト時のセッション破棄までを実装します。

Laravel PHP

PHP 8.3のcURLでISBNから書誌情報を取得する:openBD・Google Books API・NDLサーチ

2026.09.01

PHP 8.3のcURLを使い、ISBNをもとにopenBD、Google Books API、NDLサーチへアクセスする実装例を紹介します。JSON/XMLの解析、書誌情報の共通形式への変換、通信エラーや項目欠損の扱いに加え、openBD v1の終了予定やNDLサーチの利用条件も整理します。

Google Books API NDL API openBD API PHP

Laravel 13+Vue.jsで画像・CSVを非同期アップロードする:FormData・ファイル検証・非公開保存

2026.08.31

Laravel 13のBladeに組み込んだVue.jsから、FormDataを使って画像またはCSVを非同期で送信する方法を解説します。JPEG・PNGの画像検証、CSVの文字コード変換と簡易的な内容確認、20MBのサイズ制限、非公開ディスクへの保存、CSRF・バリデーションエラー処理を実装し、fetchを基本にaxiosとの違いも整理します。

JavaScript Laravel PHP Vue.js

阿部辰也へのお仕事の依頼・お問い合わせ

軽いご相談もお気軽にどうぞ!

個人情報の取り扱いについて *必須 プライバシーポリシーをご確認いただき、同意いただける場合は「同意する」にチェックをしてください。

keyboard_double_arrow_up
TOP