PHPでHTMLのDOMを操作する方法を選ぶ

2023年 3月16日 Posted 野々瀨(フロントエンドエンジニア)

PHPでスクレイピングであったり、動的にHTMLを書き換えたりなど、HTMLを解析しDOMとして操作したいときがあると思います。
そんなPHPでHTMLのDOM操作をしたい場合に使用するモジュールやライブラリを軽くご紹介します。

本記事をご覧いただくにあたって

DOMについて

PHPでDOM操作をする、あるいはしようとしていることが開始地点であるため、DOMについては知っている前提として省略します。

詳細な使い方

DOMを操作する方法がいろいろある中で、あくまで何がよいのか選択をするためで、詳細な使い方はご紹介しません。
使い方を知りたい方は、各公式のドキュメントなどをご参照ください。

DOMを操作するモジュールやライブラリ

PHPでは標準でDOM操作を行えるモジュールがあったり、ライブラリを使用してDOM操作を行ったりすることができます。
モジュールやライブラリについて次の幾つかをご紹介します。

SimpleXML

SimpleXMLは、PHP 5から追加された拡張モジュールで、XMLをオブジェクトとして簡単に操作できます。
モジュールをインストールしていれば標準で扱うことができます。
HTMLは標準で扱うことができず、一度DOMDocumentで解析して、そのオブジェクトを読み込むことで操作できるようになります。

コード例

$xml = simplexml_load_file('./data/sample.xml');

$names = array();

foreach ($xml->items->item as $item) {
	$names[] = (string)$item->name;
}

関連リンク

PHP: SimpleXML - Manual

DOMDocument

DOMDocumentは、PHP 5から追加された拡張モジュールで、XMLとHTMLをオブジェクトとして簡単に操作できます。
モジュールをインストールしていれば標準で扱うことができます。
マルチバイトを扱う場合に、数値参照に変換されてしまったりと一部不具合のようなものがありますが、工夫することで回避することはできます。

コード例

$doc = file_get_contents('./data/sample.html');

$dom = new DOMDocument();

libxml_use_internal_errors(true);

$dom->loadHTML($doc, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

libxml_clear_errors();

$title_node = $dom->getElementsByTagName('title')[0];

$title = $title_node->nodeValue;

関連リンク

PHP: DOMDocument - Manual

Simple HTML DOM Parser

Simple HTML DOM Parserは、CSSセレクターでDOM操作を行うことができるライブラリです。
他のDOM解析ライブラリなどにはあまり見ないダンプ機能が付いています。
このライブラリの欠点として、HTMLを取得する際、内部にあるインデントはそのままですが、改行は削除された状態を取得します。

コード例

require_once('simple_html_dom.php');

$dom = file_get_html('./data/sample.html');

$news_titles = array();

foreach ($dom->find('.news li') as $item) {
	$news_titles[] = $item->text();
}

関連リンク

Simple HTML DOM documentation

phpQuery

phpQueryは、JavaScriptのライブラリであるjQueryのように、CSSセレクターでDOM操作を行うことができるライブラリです。
jQueryのメソッド名がほぼそのまま使われているため、jQueryを扱ったことがあれば似た書き方が可能です。
ただし、最終リリース日が2009年と古いですので、それ以降のjQueryの機能やCSSのセレクターは扱えません。

コード例

require_once('phpQuery.php');

$dom = phpQuery::newDocumentFile('./data/sample.html');

$anchors = $dom->find('a[href^="#"]');

$links = array();

$anchors->each(function($anchor) {
	global $links;

	$links[] = pq($anchor)->attr('href');
});

関連リンク

phpquery - Google Code Archive

PHP Html Parser

PHP Html Parserは、JavaScriptのライブラリであるjQueryのように、CSSセレクターでDOM操作を行うことができるライブラリです。
他のモジュールやライブラリと違い、要素はTagというクラスで要素自身の情報をもっていて、取得はもちろんタグ名の変更などもTagで行います。
要素を作成する際はTagクラス、HtmlNodeクラス、テキストが必要であればTextNodeクラスと複数のクラスを組み合わせる必要があります。

コード例

require_once('./vendor/autoload.php');

use PHPHtmlParser\Dom;
use PHPHtmlParser\Options;
use PHPHtmlParser\Dom\Tag;
use PHPHtmlParser\Dom\Node\HtmlNode;
use PHPHtmlParser\Dom\Node\TextNode;

$dom = new Dom;

$options = new Options();
$options->setWhitespaceTextNode(false);

$dom->loadFromFile('./data/sample.html', $options);

$dom->find('.news li')->each(function($item) {
	$tag        = new Tag('span');
	$label_node = new HtmlNode($tag);
	$text_node  = new TextNode('ラベル');

	$label_node->addChild($text_node);
	$item->addChild($label_node);
});

関連リンク

paquettg/php-html-parser - GitHub

DiDOM

DiDOMは、CSSセレクターでDOM操作を行うことができるライブラリです。
style属性の解析に対応していて、取得はもちろん設定も可能です。
他のライブラリなどに比べて機能が豊富に感じます。

コード例

require_once('./vendor/autoload.php');

use DiDom\Document;

$dom = new Document();

$dom->preserveWhiteSpace(true);

$dom->loadHtmlFile('./data/sample.html');

$news_titles = array();

foreach ($dom->find('.news li') as $item) {
	$news_titles[] = $item->text();
}

関連リンク

Imangazaliev/DiDOM - GitHub

PHP Selector

PHP Selectorは、CSSセレクターでDOM操作を行うことができるライブラリです。
他のライブラリと比べ、要素の情報を取得する機能だけのシンプルなライブラリです。

コード例

require_once('selector.inc');

$doc = file_get_contents('./data/sample.html');

$dom = new SelectorDom($doc);

$news_titles = array();

foreach ($dom->select('.news li') as $item) {
	$news_titles[] = $item['text'];
}

関連リンク

tj/php-selector - GitHub

QueryPath

QueryPathは、JavaScriptのライブラリであるjQueryのように、CSSセレクターでDOM操作を行うことができるライブラリです。
jQueryのメソッド名がほぼそのまま使われているため、jQueryを扱ったことがあれば似た書き方が可能です。
style属性の解析に対応していて、取得はもちろん設定も可能です。

コード例

require_once('./vendor/autoload.php');

$qp = qp('./data/sample.html');

$news_titles = array();

$qp->find('.news li')->each(function($item) {
	global $news_titles;

	$news_titles[] = $item['text'];
});

関連リンク

technosophos/querypath - GitHub

HTML5DOMDocument

HTML5DOMDocumentは、DOMDocumentでHTML5を扱う際の不具合などを解消したライブラリです。
DOMDocumentの機能はそのままに、JavaScriptにあるquerySelector / querySelectorAllメソッド

といった機能も備わっています。

コード例

require_once('./vendor/autoload.php');

$dom = new IvoPetkov\HTML5DOMDocument();

$dom->loadHTML($doc, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

$news_titles = array();

foreach ($dom->querySelectorAll('.news li') as $item) {
	$news_titles[] = $item['text'];
}

関連リンク

ivopetkov/html5-dom-document-php - GitHub

簡単な比較

項目SimpleXMLDOMDocumentSimple HTML DOM ParserphpQueryPHP Html ParserDiDOMPHP SelectorQueryPathHTML5DOMDocument
最終リリース日 - - 2019/10/20 2009/5/2 2020/11/2 2022/5/8 2014/10/20 2016/8/2 2022/4/26
バージョン - - 1.9.1 0.9.5 3.1.1 2.0.0 1.1.5 3.0.5 2.3.1
PHPバージョン 5~8 5~8 5.6.0~ 5系~(注1) 7.2.0~7.4.0、 8.0.x~(注2) 7.2.0~、 8.0.x~(注2) 5.2.8~、 8.0.x~(注2) 5.2.0~、 8.0.x~(注2) 7.0.x~8.1.0
マークアップ言語 XML XML、 HTML、 XHTML、 HTML5 XML、 HTML、 XHTML、 HTML5 XML、 HTML、 XHTML、 HTML5 XML、 HTML、 XHTML、 HTML5 XML、 HTML、 XHTML、 HTML5 XML、 HTML、 XHTML、 HTML5 XML、 HTML、 XHTML、 HTML5 XML、 HTML、 XHTML、 HTML5
文字コード UTF-8、 Shift_JIS、 他 UTF-8、 Shift_JIS、 他 UTF-8、 Shift_JIS、 他 UTF-8、 Shift_JIS、 他 UTF-8、 他 UTF-8、 Shift_JIS、 他 UTF-8、 Shift_JIS、 他 UTF-8、 Shift_JIS、 他 UTF-8、 Shift_JIS、 他
操作可能なノード 要素、 属性、 CDATA、 他 要素、 属性、 テキスト、 CDATA、 他 要素、 属性、 テキスト、 他 要素、 属性、 テキスト、 他 要素、 属性、 テキスト、 他 要素、 属性、 テキスト、 他 要素、 属性、 テキスト 要素、 属性、 テキスト、 他 要素、 属性、 テキスト、 CDATA、 他
ノードの取得(検索)方法 アロー演算子でたどる、 xPath ID名、 タグ名、 xPath CSSセレクター、 ID名、 タグ名 CSSセレクター CSSセレクター、 ID名、 タグ名 CSSセレクター、 xPath CSSセレクター CSSセレクター、 xPath ID名、 タグ名、 xPath、 CSSセレクター
マークアップ全体の出力 あり あり あり あり あり あり なし あり あり
ファイルへの直接保存 あり あり あり なし なし なし なし なし あり

選定

ネイティブで扱うならDOMDocument、DOMDocumentにおいて自身で一部のエラー解消が不安な場合はHTML5DOMDocument、ライブラリなら操作が簡単で多機能な更新もされているDiDOM(PHP 5系で動かすならQueryPath)といったところでしょうか。

筆者は昔、ライブラリではやりたいことが実現できなかったため、DOMDocumentで開発していました。
最近(というほどでもないですが)はライブラリが豊富に展開され、実現できることがかなり増え、操作も簡単になっているため、より実装しやすくなっています。