PHPでHTMLをパースして解析する簡単な方法

今、個人的に管理しているサイトのHTMLが正しく出力されているかを確認するためのスクリプトを書いています。そのために、出力されたHTMLを解析して、中に適切な情報が含まれているかを知る必要が出てきました。

そういうわけで、いわゆるスクレイピングのようなことをしたくなってきました。

HTMLの解析はなかなか大変

これがJSONデータを解析するとかなら良いのですが、HTMLを細かく解析するのはなかなか大変です。

なぜなら、HTMLというのは結構みんな自由に書かれてしまっているからです。それでいて、そんな自由に書かれたHTMLもブラウザ上は動いてしまうため、それで良しとされてしまっている。時に中身は最悪だったりします。コンピュータが処理するには、厳密性に欠けるデータなんですね。

simplexml_load_stringでは解析できない。

「PHPに便利な関数ないかなー」と探していると、simplexml_load_stringという関数がありました。これは、XML文書をパースしてオブジェクト化する関数です。これで、HTMLも解析してくれないかな・・・と試してみます。

$html = file_get_contents('http://blog.katty.in/');
$xmlObject = simplexml_load_string($html);
var_dump($xmlObject);

結果としては惨敗。

bool(false)

失敗コードが返ってきました。HTMLで書かれているものって閉じカッコが無かったり、入れ子構造がおかしくなっていたりするので、仕方ないといえば仕方ない・・・。

HTMLを整形できるメソッドがあった

もう少し調べてみると、PHP5から追加されたクラスですが、 DOMDocument::loadHTML を使えば、HTMLをXMLとして適切に整形できるらしいことを知りました。

$domDocument = new DOMDocument();
$domDocument->loadHTML($html);
$xmlString = $domDocument->saveXML();

こんな感じで、HTMLを整形できます。

整形前

<!DOCTYPE html>
<html dir="ltr" lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>三度の飯とエレクトロン</title>
<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="stylesheet" type="text/css" media="all" href="http://blog.katty.in/wp-content/themes/twentyeleven/style.css" />
<link rel="pingback" href="http://blog.katty.in/xmlrpc.php" />
<!--[if lt IE 9]>
<script src="http://blog.katty.in/wp-content/themes/twentyeleven/js/html5.js" type="text/javascript"></script>
<![endif]-->
<link rel="alternate" type="application/rss+xml" title="三度の飯とエレクトロン &raquo; フィード" href="http://blog.katty.in/feed" />
<link rel="alternate" type="application/rss+xml" title="三度の飯とエレクトロン &raquo; コメントフィード" href="http://blog.katty.in/comments/feed" />

整形後

<!DOCTYPE html>
<html dir="ltr" lang="ja"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width"/><title>&#xE4;&#xB8;&#x89;&#xE5;&#xBA;&#xA6;&#xE3;&#x81;&#xAE;&#xE9;&#xA3;&#xAF;&#xE3;&#x81;&#xA8;&#xE3;&#x82;&#xA8;&#xE3;&#x83;&#xAC;&#xE3;&#x82;&#xAF;&#xE3;&#x83;&#x88;&#xE3;&#x83;&#xAD;&#xE3;&#x83;&#xB3;</title><link rel="profile" href="http://gmpg.org/xfn/11"/><link rel="stylesheet" type="text/css" media="all" href="http://blog.katty.in/wp-content/themes/twentyeleven/style.css"/><link rel="pingback" href="http://blog.katty.in/xmlrpc.php"/><!--[if lt IE 9]>
<script src="http://blog.katty.in/wp-content/themes/twentyeleven/js/html5.js" type="text/javascript"></script>
<![endif]--><link rel="alternate" type="application/rss+xml" title="&#xE4;&#xB8;&#x89;&#xE5;&#xBA;&#xA6;&#xE3;&#x81;&#xAE;&#xE9;&#xA3;&#xAF;&#xE3;&#x81;&#xA8;&#xE3;&#x82;&#xA8;&#xE3;&#x83;&#xAC;&#xE3;&#x82;&#xAF;&#xE3;&#x83;&#x88;&#xE3;&#x83;&#xAD;&#xE3;&#x83;&#xB3; &#xBB; &#xE3;&#x83;&#x95;&#xE3;&#x82;&#xA3;&#xE3;&#x83;&#xBC;&#xE3;&#x83;&#x89;" href="http://blog.katty.in/feed"/><link rel="alternate" type="application/rss+xml" title="&#xE4;&#xB8;&#x89;&#xE5;&#xBA;&#xA6;&#xE3;&#x81;&#xAE;&#xE9;&#xA3;&#xAF;&#xE3;&#x81;&#xA8;&#xE3;&#x82;&#xA8;&#xE3;&#x83;&#xAC;&#xE3;&#x82;&#xAF;&#xE3;&#x83;&#x88;&#xE3;&#x83;&#xAD;&#xE3;&#x83;&#xB3; &#xBB; &#xE3;&#x82;&#xB3;&#xE3;&#x83;&#xA1;&#xE3;&#x83;&#xB3;&#xE3;&#x83;&#x88;&#xE3;&#x83;&#x95;&#xE3;&#x82;&#xA3;&#xE3;&#x83;&#xBC;&#xE3;&#x83;&#x89;" href="http://blog.katty.in/comments/feed"/>

整形してからsimplexml_load_stringを使えば解析できる

そういうわけで、整形してからsimplexml_load_stringを使えば無事に解析することができました。

$html = file_get_contents('http://blog.katty.in/');
$domDocument = new DOMDocument();
$domDocument->loadHTML($html);
$xmlString = $domDocument->saveXML();
$xmlObject = simplexml_load_string($xmlString);
var_dump($xmlObject);
object(SimpleXMLElement)#2 (4) {
  ["@attributes"]=>
  array(2) {
    ["dir"]=>
    string(3) "ltr"
    ["lang"]=>
    string(2) "ja"
  }
  ["comment"]=>
  object(SimpleXMLElement)#3 (0) {
  }
...

連想配列にしてしまえばHTML内の要素を取り出すのも簡単

あとは煮るなり焼くなりですが、連想配列にしてしまえば更に簡単に扱えます。たとえば次のように、読み込んで解析したオブジェクトからheadタグ内のtitleタグ内の値を取り出せば、そのページのタイトルを取り出すなどできます。

$array = json_decode(json_encode($xmlObject), true);
echo $array['head']['title'];
三度の飯とエレクトロン

文字化け対策

XMLへの変換に関して文字化けが発生する場合があるようです。

DOMDocument::loadHTMLで文字化けする場合

一手間加えれば解決するので、それでも楽ですね。

About katty0324

Scroll To Top