HTMLをパースしてきてDB化したり、RSSのテンプレに落とし込む…と言ったことを少し前からしているのですけど、1つ2つならいざ知らず対象サイトが増えてくると正規表現で何かし続けるのはたいそう無理があります。そういうわけでDOMを使ったりXPathを使ったりもしていたのですが、どちらもHTMLの構造にぴったりとは言えなくて、高度なことが出来るが故に記述が複雑になってしまうのでした。うーん。慣れるまでにハードルがあるな…

そんなことを思っていたのですが、ふとした拍子に見つけたライブラリ「PHP Simple HTML DOM Parser」を利用してみたらば、とてつもなく便利でびっくり。jQueryのようなセレクタでガンガン指定して持ってくるスタイルでわかりやすいのなんの。

$text = $dom->find('#todays_darling', 0)->innertext;  //今日のダーリンがHTML込みで取得できます

出来れば野良ライブラリを使うようなことはしたくなかったのですがこれは仕方がないです。ホントに。そういうわけで今さらながら軽くご紹介を。

PHP Simple HTML DOM Parser

以下、具体的なコードを交えつつ使い方など。

ライブラリを読み込み

require_once 'simple_html_dom.php';



データを読み込む

読み込む方法は次の2つ。
返り値はどちらもsimple_html_domオブジェクトです(失敗するとfalseが返る)。

HTMLを読み込むときはstr_get_html

$dom = str_get_html('<html><body>Hello!</body></html>');

ファイルやURLから読み込むときはfile_get_html

$dom = file_get_html('http://www.1101.com/home.html');
$dom = file_get_html('test.htm');

file_get_htmlは内部的にはfile_get_contentsを使用しています。その後のデータ読み込み部分はstr_get_htmlと同じ。


上記が標準っぽいですが、最初にオブジェクトを作成しておいて読み込む方法もあります。

$dom = new simple_html_dom();

HTMLを読み込むときはloadメソッド

$dom->load('<html><body>Hello!</body></html>');

ファイルやURLから読み込むときはload_fileメソッド

$dom->load_file('http://www.1101.com/home.html');
$dom->load_file('test.htm');



読み込んだデータから要素を抽出する

IDで検索

$dom->find('#todays_darling');

クラスで検索

$dom->find('#menu .menu_01');

タグで検索

$dom->find('#menu .menu_01 img');

属性でフィルタリング

$dom->find('td[align=center]');  //align属性がcenterのtd
$dom->find('a[!class]');         //class属性を持たないa

特定の表示順

$dom->find('#sub_menu li', 1);  //ID:sub_menuの2番目のli



出力する

出力には「plaintext」「innertext」「outertext」という3つのメソッドが用意されています。
僕がこのライブラリを選択した理由もここです。

サンプル

$dom->load('<h1>タイトル<strong>強調</strong></h1>')

plaintext

タグを除いて出力されます。
$dom->find('h1', 0)->plaintext;  //タイトル強調

innertext

指定したタグの中身がHTMLタグ込みで出力されます。
$dom->find('h1', 0)->innertext;  //タイトル<strong>強調</strong>

outertext

指定したタグとその中身がHTMLタグ込みで出力されます。
$dom->find('h1', 0)->outertext;  //<h1>タイトル<strong>強調</strong></h1>



DOM操作

DOM操作用のメソッドも定義されています。

getAllAttributes()
getAttribute($name)
setAttribute($name, $value)
hasAttribute($name)
removeAttribute($name)
getElementById($id)
getElementsById($id, $idx=null)
getElementByTagName($name)
getElementsByTagName($name, $idx=null)
parentNode()         //parent()でも同じ
childNodes($idx=-1)  //children($idx)でも同じ
firstChild()         //first_child()でも同じ
lastChild()          //last_child()でも同じ
nextSibling()        //next_sibling()でも同じ
previousSibling()    //prev_sibling()でも同じ



注意

file_get_html()を繰り返し利用するときはメモリリークが発生し、そのまま続けると「セグメンテーション違反です」「zend_mm_heap corrupted」が発生してPHPプロセスが停止してしまうので、その回避のために意図してメモリを解放する必要があります。具体的には処理が終わったらオブジェクトに対して次の処理を行います。
$dom->clear();  //オブジェクトに含まれる全ての要素を削除
unset($dom);    //オブジェクトを削除
clearメソッドはデストラクタなどで呼ばれるようになっていますが、繰り返すときは自分で呼んどけ、ということでしょうかね。ちょっとはまりました。