名前空間やCDATAの読み込み

PHPでRSS取得するのに、単純にsimplexml_load_file()を利用して取得しようと思った時に、名前空間(「:」コロンで区切られた要素)やCDATAの値が取得できないのでメモ。 CDATAに関して言えば、取得自体はできているけど、パッと見取れていないように見えるのでちょっと工夫が必要。

WordPressの基本的なfeed(RSS2.0)を読み込む例(記事部分抜粋)

<item>
<title>記事タイトル</title>
<link>http://xxx.jp/?p=xxx</link>
<pubDate>Tue, 18 Feb 2014 01:00:43 +0000</pubDate>
<dc:creator>記事を書いた人</dc:creator>
<category><![CDATA[ 未登録 ]]></category>
</item>

このファイルを読み込んでみます。

simplexml_load_file()で取得してみる

$rss_url   = "http://xxx.jp/?feed=rss2";
$rss_data  = simplexml_load_file( $rss_url );
$rss_array = array();
$i         = 0;

foreach ( $rss_data->channel->item as $item ) {
    $rss_array[$i]['title']    = $item->title;
    $rss_array[$i]['link']     = $item->link;
    $rss_array[$i]['pubData']  = $item->pubData;
    $rss_array[$i]['dc']       = $item->dc:creator;    // 取得できない
    $rss_array[$i]['category'] = $item->category;      // 取得できない
    $i++;
}

これだけだと、dc:creatorとcategoryが取得できません。

CDATAの取得を考える

CDATAの取得方法を考えてみました。

CDATA取得方法その1:simplexml_load_file()のパラメータを設定する

simplexml_load_file()でLIBXML_NOCDATAを第3引数に渡すことで取得できます。

$rss_url   = "http://xxx.jp/?feed=rss2";
$rss_data  = simplexml_load_file( $rss_url, 'SimpleXMLElement', LIBXML_NOCDATA );
$rss_array = array();
$i         = 0;

foreach ( $rss_data->channel->item as $item ) {
    $rss_array[$i]['title']    = $item->title;
    $rss_array[$i]['link']     = $item->link;
    $rss_array[$i]['pubData']  = $item->pubData;
    $rss_array[$i]['dc']       = $item->dc:creator;  // 取得できない
    $rss_array[$i]['category'] = $item->category;    // 取得できた!
    $i++;
}

libxmlはPHPのXML操作ライブラリで、その中の定義済み定数にLIBXML_NOCDATAの説明があります。他の定数も存在するので、XMLの操作をする上で一度は見ておいたほうが良いかと。

CDATA取得方法その2:CDATA要素を(string)でキャストする

CDATA要素に対して文字列として扱うように、(string)でキャストすると取得できます。

$rss_url   = "http://xxx.jp/?feed=rss2";
$rss_data  = simplexml_load_file( $rss_url );
$rss_array = array();
$i         = 0;

foreach ( $rss_data->channel->item as $item ) {
    $rss_array[$i]['title']    = $item->title;
    $rss_array[$i]['link']     = $item->link;
    $rss_array[$i]['pubData']  = $item->pubData;
    $rss_array[$i]['dc']       = $item->dc:creator;        // 取得できない
    $rss_array[$i]['category'] = (string)$item->category;  // 取得できた!
    $i++;
}

CDATA要素の数が沢山あると大変なので、「その1」のやり方がスマートのように思えます。

名前空間要素の取得を考える

次に名前空間要素の取得方法を考えてみました。

名前空間要素の取得方法その1:children()メソッドで名前空間名を指定

「:」コロンつなぎの要素、名前空間情報は、children()メソッドで指定すると取得できます。(PHPのマニュアル:SimpleXMLElement::childrenを参照)

$rss_url   = "http://xxx.jp/?feed=rss2";
$rss_data  = simplexml_load_file( $rss_url, 'SimpleXMLElement', LIBXML_NOCDATA );
$rss_array = array();
$i         = 0;

foreach ( $rss_data->channel->item as $item ) {
    $rss_array[$i]['title']    = $item->title;
    $rss_array[$i]['link']     = $item->link;
    $rss_array[$i]['pubData']  = $item->pubData;
    $rss_array[$i]['dc']       = $item->children('dc',true)->creator;  // 取得できた!
    $rss_array[$i]['category'] = $item->category;                         // 取得できた!
    $i++;
}

名前空間要素の取得方法その2:記事の要素をまるごと配列に保存

これが一番やりたかった事。RSSの要素をWordPress側でカスタマイズする必要があったので、CDATAも名前空間の要素もどういうのが来るか不明なので、まるごと配列にえいやー!と保存したい。 とりあえず、何も考えずにまるごと保存してみました。

$rss_url   = "http://xxx.jp/?feed=rss2";
$rss_data  = simplexml_load_file( $rss_url, 'SimpleXMLElement', LIBXML_NOCDATA );
$rss_array = array();

foreach ( $xmldata->channel->item as $item ) {
    $rss_array[] = $item;
}

CDATAはLIBXML_NOCDATA指定により取得できました。けど、名前空間が……となったので、一度RSS情報を文字列として取得して、名前空間要素を文字列変換しパースするという方法で解決しました。

$rss_url   = "http://xxx.jp/?feed=rss2";
$rss_data  = file_get_contents( $rss_url );
$rss_data  = preg_replace( "/<([^>]+?):(.+?)>/", "<$1_$2>", $rss_data );
$rss_data  = preg_replace( "/_\/\//", "://", $rss_data );
$rss_data  = simplexml_load_string( $rss_data, 'SimpleXMLElement', LIBXML_NOCDATA );
$rss_array = array();

foreach ( $rss_data->channel->item as $item ) {
    $rss_array[] = $item;
}

\(^o^)/取得できた!!

  1. file_get_contents()でRSS情報を取得する
  2. preg_replace()で「:」(コロン)を「_」(アンダーバー)に変換
  3. preg_replace()でプロトコルは元に戻す
  4. simplexml_load_string()で文字列XML情報をパースする

注意点

以下の2点に注意すること。

  • simplexml_load_file関数はphp.iniの設定でallow_url_fopenをOnにしていないと使えない。
  • Offの場合は、curl_get_contents()でRSS情報を取得してから、simplexml_load_string()でパースし直す。