阿部寛のホームページをPHPでDOMる


HTML を正しい文字コードで DOMDocument にしたい

DOMDocument が勝手に meta 検出してくれたら良いのだけど。
とりあえず自前で文字コード検出して、うまく変換して、DOMDocument を作る実験。

阿部寛のホームページが安定して昔ながらの文字コードを使ってくれているので実験材料に良い。
http://abehiroshi.la.coocan.jp/

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=x-sjis">
<title>阿部寛のホームページ</title>
</head>
<frameset cols=18,82>
  <frame src=menu.htm marginheight=0 marginwidth=0 scrolling=auto name=left>
  <frame src=top.htm marginheight=0 marginwidth=0 scrolling=auto name=right>
</frameset><noframes></noframes>

そもそも frame が懐かしい。

作成したコード

<?php
// HTML全体からtitleを取得
function html2title($html){
    // まずは UTF-8 で決め打ち
    $htmlEncoded = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
    $doc = new DOMDocument();
    @$doc->loadHTML($htmlEncoded);

    // metaからcharset検出
    $charset = '';
    $elements = $doc->getElementsByTagName("meta");
    for($i = 0; $i < $elements->length; $i++){
        $e = $elements->item($i);

        // charset属性をチェック
        // <meta charset="utf-8"/>
        $node = $e->attributes->getNamedItem("charset");
        if($node){
            $charset = $node->nodeValue;
            break;
        }

        // http-equiv属性をチェック
        // <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        $node = $e->attributes->getNamedItem("http-equiv");
        if($node && strcasecmp($node->nodeValue, 'content-type') == 0){
            $node = $e->attributes->getNamedItem("content");
            if($node && preg_match('/[\; ]charset ?\= ?([A-Za-z0-9\-\_]+)/', $node->nodeValue, $m)){
                $charset = $m[1];
                break;
            }
            continue;
        }
    }

    // 検出されたcharsetがUTF-8じゃなかったら
    if($charset !== '' && !preg_match('/^utf\-?8$/i', $charset)){
        // 文字コード変換し直して
        $htmlEncoded = mb_convert_encoding($html, 'HTML-ENTITIES', $charset);
        // DOMも構築し直す
        $doc = new DOMDocument();
        @$doc->loadHTML($htmlEncoded);
    }

    // title取得
    $elements = $doc->getElementsByTagName("title");
    for($i = 0; $i < $elements->length; $i++){
        $e = $elements->item($i);
        return $e->textContent;
    }

    // titleが見つからなかった場合
    return false;
}

// -- -- 実験コード -- -- //
// x-sjis の例
$body = file_get_contents("http://abehiroshi.la.coocan.jp/");
$title = html2title($body);
echo "title = $title\n";

// euc-jp の例
$body = file_get_contents("http://d.hatena.ne.jp/");
$title = html2title($body);
echo "title = $title\n";

// Shift_JIS の例
$body = file_get_contents("http://www.tohoho-web.com/www.htm");
$title = html2title($body);
echo "title = $title\n";

// UTF-8 の例
$body = file_get_contents("http://qiita.com/");
$title = html2title($body);
echo "title = $title\n";

実行結果

title = 阿部寛のホームページ
title = はてなダイアリー - 写真・画像・動画付き日記を無料で
title = とほほのWWW入門
title = Qiita - プログラマの技術情報共有サービス

はてなダイアリーがいまだに euc-jp だったというのが意外。

関連

おわり

おそらくこれまでに何千人もが同じようなコード書いてるような気がするわけで、
DRY原則に真っ向から立ち向かってしまっている感なんだけど、
良いサンプルが見つからなかったので自分で作ってしまった。

もっとスマートな(というかデファクトでかつ安心できる)方法があれば知りたいです。。。