MACH3

MACH3.laBlog

WEB屋の音速実験室

PHPでXMLの名前空間つきタグを読み込む色々

PHPのSimpleXMLでRSSを読み込もうと思ったら、
名前空間つきのタグが読み込めなかったので、色々な方法を試してみた備忘録。

PHPでXMLの名前空間つきタグを読み込む色々

実験の対象として、はてなブックマークのホットエントリのRSSを使ってみます。

$url = "http://b.hatena.ne.jp/hotentry.rss";
$str = file_get_contents( $url );

SimpleXMLを使う方法

普通にSimpleXMLで読み込むと、<dc:date>や<hatena:bookmarkcount>など、
名前空間プレフィックスつきのタグを読み込むことが出来ません。

$simplexml = simplexml_load_string( $str );
var_dump( $simplexml );

消えてしまう名前空間プレフィックスつきタグ

SimpleXMLで名前空間つきタグを扱うには、childrenメソッドを使います。
例えば、<hatena:bookmarkcount>の値を取得する場合、
次のように掘っていく必要があります。

$simplexml->item[$i]->children("hatena", true)->bookmarkcount;

ループでitem要素全てまとめて処理してみます。
それぞれのitem要素にhatena->bookmarkcountが追加されます。

$simplexml = simplexml_load_string( $str );
$i = count( $simplexml->item ); 
while( $i-- ){
    $simplexml->item[$i]->hatena->bookmarkcount = $simplexml->item[$i]->children("hatena", true)->bookmarkcount;
}
var_dump( $simplexml );

DOMDocumentクラスを使う

DOM文書を読み込むためのDOMDocumentクラスを使ってみます。
JavaScriptでも使うような、getElementsByTagNameなんていうお馴染みのメソッドを使って
文書構造を掘っていきます。

$dom = DOMDocument::loadXML( $str );
foreach( $dom->getElementsByTagName("item") as $item ){
    foreach( $item->childeNodes as $node ){
        if( $node->nodeType === 1 ){
            echo "{$node->nodeName} : {$node->nodeValue}";
        }
    }
}

nodeTypeには、ノードの種類が整数で渡されています。
上で言う「1」はエレメントノードですね。
cf) PHP: 定義済み定数 – Manual
勿論、プレフィックスつきのタグも問題なく読み込めます。

出力時に楽なように、エントリーをまとめて一つの配列に突っ込んでみます。

$dom = DOMDocument::loadXML( $str );
$entries = array();
foreach( $dom->getElementsByTagName( "item" ) as $item ){
    $entry = array();
    foreach( $item->childNodes as $node ){
        if( $node->nodeType !== 1 ) continue;
        if( $item->getElementsByTagName( $node->nodeName )->length > 1 ){
            if( !isset( $entry[ $node->nodeName ] ) ){
                $entry[ $node->nodeName ] = array();
            }
            array_push( $entry[ $node->nodeName ], $node->nodeValue );
        } else {
            $entry[ $node->nodeName ] = $node->nodeValue;
        }
    }
    array_push( $entries, $entry );
}
var_dump( $entries );

複数あるタグは配列で対応。
書いてみたものの…
DOMDocumentのまま使った方が使いやすい気もしたり。
むしろこういう使い方するならSimpleXML使った方がよいですね。

XMLReaderクラスを使う

SimpleXMLやDOMDocumentが、XMLの情報をドッカンと丸ごとメモリに置いておくのに大して、
XMLReaderはfopenのようにポインタの位置だけを保持しておく為、メモリに優しく高速。らしい。
ですが、ポインタの位置だけだと正直使い辛いのも事実。

$xml = XMLReader::load( $url, null, LIBXML_NOBLANKS );
while( $xml->read() ){
    echo "{$xml->nodeType} : {$xml->name} = {$xml->value} @ {$xml->depth} <br />";
}

$xml->read()でノードを一つずつ読み進んでいく感じ。
多階層なXMLを読んでいくのは骨が折れそう。
これは重た~いXMLを読まなきゃいけない時用の代物ですね。

まとめ

名前に違わず相当楽ですね、SimpleXML。
名前空間つきのタグも処理したい場合等は、DOMDocumentの方がシンプルに処理出来そう。
XMLReaderはパフォーマンスを意識したい場合に。

上手に使い分けたいですね!


2010/12/18
マニュアルのリンクを修正
Share |

コメントを投稿する

入力されたメールアドレスは公開されません。


*

「東北地方太平洋沖地震」義援金支援(Yahoo!基金)