simplexmlを使ってみて

2009/03/03 | XML

とあるお仕事でXMLを扱うことになって、simplexmlを使っているのだが、使っていて思ったことをいくつか。

クライアントからいただいたファイルをみてちょっと唖然。XML自体は構造もしっかりしているのだが、タグは全て日本語。これはすごく萎えた。日本人だから・・・しょうがないんだよね。でも、これっていろんなソフトで加工するときに障害にならないのか不安に思えた。そして萎えたことがもう一つ。文字コードシフトJISじゃんかー。これも、まぁ、日本人だから・・・しょうがないんだよね。

ということでシフトJISでマルチバイトなタグで構成されたXMLファイルがsimplexmlで扱えるのか試してみた。結果からいうと「すごい!」「使える!」。まず普通にsimplexml_load_fileとかして、var_dumpしてみた。ちゃんと全部オブジェクトに格納されている!。と、ひとまず感動した。

そして文字コードはというと、UTF-8に変換されて格納されている!。扱いやすい!。XMLファイルの先頭に文字コードの宣言(もちろんShift_JISと指定)がされているのだが、ここを判断して(もしくは文字コード自動判別で)、変換されているんだろう。「自動」というのはいささか怖い気もするが、それでもまぁ満足がいく。

さて、各タグにアクセスできるかどうか、だがこれもOK。以下のように記述して、アクセスすることができた。

$xmlObj->{‘日本語のタグ名’}

正直ほっとした。シフトJISでマルチバイトなタグのXMLを見たときはどうなることかと思ったが何とかなりそうだ。

1点気づいたことがあった。いったんsimplexml_load_fileして、再度asXML()でXMLを吐き出すときにXML宣言がなくなっちゃってしまったことだ。これはそういう仕様なんだろうか。そもそもXML宣言部分は、XML本体とは別物という扱いなんだろうか。ここを再現することはできないんだろうか。

まぁ、かっこいいプログラムである必要はない。要求どおりに動いてくれさえすればいいので、XML宣言は後付することにする。何はともあれいろいろ発見できた一日だった。

simpleXMLがすごく簡単

2009/02/22 | XML

PHP4の時代はXMLを扱うのが簡単ではなかった。必ず何かツールを使わないといけなかった。そんなPHP4も去年ついに開発を終了したので、ようやく重い腰を上げてPHP5に移行。超使いやすくなったといわれているPHP5のXML関連関数を今頃使い始めたのだが、これがまた簡単。

simplexml_load_fileとかするだけで、XMLツリーをオブジェクトに格納してくれる。

しかもオブジェクト内を書き換えてXMLを吐き出すこともできる。もっと早くに乗り換えていればよかったよ、PHP5。ただ唯一XMLのタグ名にコロン「:」があるとうまく扱えないことが気になった。最初、楽天ウェブサービスAPIからの戻り値を関数に放り込んでvar_dumpしたけど、オブジェクト内にぜんぜんメンバ変数が登録されていなくて、「手ごわい」と思ったんだけど。いろいろ実験して、ウェブ調べたらコロンが原因だってわかった。前向きな解決策はないようだけど、処理前に単にコロンを他の文字列に置き換えればOK。スマートではないけれど、これが確実っぽい。

時間に余裕ができたら、これを使って各種APIで遊んでみたい。


					

simplepie.incの問題

2007/10/25 | XML

普段よく使用しているRSSをパースするためのライブラリsimplepieに問題があることがわかった。「問題」といってもsimplepie自体に問題があるのではなく、simplepieを利用する環境(PHP)側によって問題が発生することがあるようだ。

今回某サーバでsimplepieを使ってRSSのパースを試みた。他のサーバで動作していたものを持ってきて、必要部分だけ書き換えて出来上がり、と思ったらエラーがいっぱい吐き出された。エラーは2種類。

Warning: Compilation failed: unrecognized character after (? at offset 63 in /var/www/…/simplepie.inc on line 9488

Warning: Compilation failed: characters with values > 255 are not yet supported in classes at offset 31 in /var/www/…/simplepie.inc on line 9607

該当する箇所をみてみると、前者はpreg_replace関数で、後者はpreg_match関数でエラーが出ている。ウェブで検索すると、simplepieの作者のコメントが出ていた。「PCREのバグかもしれない」とのこと。どういう状態のときにこのエラーが発生するのか、ということには言及されていないけれど、まぁ、そういうことなんだ、と納得した。

で、対応。
前者のエラーは以下のような関数の中にある。
function uncomment_rfc822($data)
{
 if ((version_compare(PHP_VERSION, ‘4.4.6’, ‘<=’) && version_compare(PHP_VERSION, ‘5’, ‘>’)) || version_compare(PHP_VERSION, ‘5.2.2’, ‘<=’))
 {
  return $data;
 }
 else
 {
  return preg_replace(‘/((?:(?:¥¥¥¥”|[^(“])*(?:”(?:[^”¥¥¥¥¥r]|¥¥¥¥.)*”¥s*)?)*)((?>!¥¥¥¥)¥((?:(?2)|.)*?(?>!¥¥¥¥)¥))/’, ‘$1′, $data);
 }
}

とりあえず「チェックしない」と決め打ちしてif節の前に「return $data;」をおいて解決。

後者はこんな関数。
function is_isegment_nz_nc($string)
{
 return (bool) preg_match(‘/^([A-Za-z0-9¥-._~¥x{A0}-¥x{D7FF}¥x{F900}-¥x{FDCF}¥x{FDF0}-¥x{FFEF}¥x{10000}-¥x{1FFFD}¥x{20000}-¥x{2FFFD}¥x{30000}-¥x{3FFFD}¥x{40000}-¥x{4FFFD}¥x{50000}-¥x{5FFFD}¥x{60000}-¥x{6FFFD}¥x{70000}-¥x{7FFFD}¥x{80000}-¥x{8FFFD}¥x{90000}-¥x{9FFFD}¥x{A0000}-¥x{AFFFD}¥x{B0000}-¥x{BFFFD}¥x{C0000}-¥x{CFFFD}¥x{D0000}-¥x{DFFFD}¥x{E1000}-¥x{EFFFD}!$&¥'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u’, $string);
}

こちらもチェックをやめて「return true;」に書き換え。これで解決。
いや、まぁ、本当に「解決した」とは言えないのだが、とりあえずこれでよしとする。
おそらくバージョンによる問題だと思うので、比較的新しいものを使っている場合はこんなことしなくても、元々エラーなんて出ないと思う。たぶん、ごく一部の環境だけだ。

しかしそんな環境を使っている自分がうらめしい・・・。

xmlrpcブログ投稿時の投稿時刻指定

2007/09/13 | XML

あいもかわらずxml-rpcで遊んでいる今日この頃(生活も厳しくなってきたT_T)。ブログ投稿時に、投稿時刻をプログラムを動作させたその日時以外を指定したい場合が出てきた。つまり、例えば「投稿日時を過去の日付を指定したい」といった具合だ。iso8601のフォーマットがどうとかこうとか書いてあるウェブサイトはいっぱい見つけたけど、「じゃぁ、実際どうすりゃいいのよ!」というところで放置していた。
今日ふと思い立ってやってみたら、簡単に出来た。でも忘れそうなのでメモしておく。ちなみに使用するのはxmlrpc.inc(http://phpxmlrpc.sourceforge.net/)。

まず実際のデータから。記事投稿時はもちろんXMLでサーバにデータを送るわけだが、そこはこんなXMLになる。
<dateTime.iso8601>20060418T21:00:21<dateTime.iso8601>

上記の真ん中のデータはUTC指定で、日付(年月日)と時刻(時:分:秒)を「T」でつないだもの、ということになる。2007年12月31日23時59分59秒なら
「20071231T14:59:59」
という値が入るはずだ(日本は9時間早いのだからUTCの時刻表記では9時間引いておく)。

これを手動で計算するのが一番いい。ちなみにPHP5ではiso8601フォーマットで日時を返すdate関数のオプションが用意されているらしいのだが、自分の環境は全てPHP4なので、ここでは使えない。でもxmlrpc.incには相応の関数が用意されていた。こんなの。

function iso8601_encode($timet, $utc=0)

戻り値はUTCの時刻だ。引数$timetは自分が指定したい日時のユニックスタイムスタンプ値。これを直接UTCで指定した場合は次の引数$utcに1を指定する。デフォルトは0で与えられたタイムスタンプから自動的にUTCに変換した値が返される。簡単お手軽関数だ。

でも、実は注意が必要で、サーバの「タイムゾーンのオフセット秒数」を使っているので、海外サーバで日本向けサービスを・・・なんて場合にはヘンテコリンな時刻になってしまう。どうせなら

function iso8601_encode($timet, $offset=0)

みたいにして、オフセットの時刻で指定できるようにすればよかったのに・・・。てことでここの関数は簡単なので、すぐ自作してしまおうっと。

APIとPHPのまとめ

2007/09/11 | XML

最近はYahooやAmazon、楽天といった各種ウェブサービス(API)を絡めた開発を多くやっている(といっても、仕事が暇なので自分用サイトとかアフィリエイト用サイトを数多く作っているのだが・・・)。またAPIを提供する会社もたくさん増えてきたので、これから本業でもそういう仕事が増えていくと思っている。実際、最近新規で受けたり相談されている仕事もウェブサービス絡みだ。そこでウェブサービス利用の「傾向と対策」みたいな、受験勉強的メモを残しておくことにする。

(0) APIを利用するために
各種APIを利用するためにはいろいろな手順を踏まないといけないことが多い。例えば、Yahooの開発者向けAPIを利用するためには「Yahoo JAPAN IDを取得」「アプリケーションIDを取得」などの手順を踏む必要があるし、さらに一日あたりのアクセス回数上限なども気にする必要がある。もちろん利用規約も守らなくてはならない。実際にプログラムを組む前に、API毎に諸々調べておく必要がありそうだ。

(1) APIで必要な知識
APIを使用するに当たって必要な知識がいくつかある。知識といっても難しいことではなく、プログラムのスキルと言ってもいいだろう。必要なスキルは以下の3つくらいに分けられる。
・データを取得する
・取得したXMLをPHPで解釈する
・一日のアクセス上限回数を越えないよう制御する

(1)-1.データを取得する
データの取得は、最近ではRESTが一般的だ。xmlrpcやsoapを使うものもあるが、簡単と言う点からか、RESTが広く普及してきたように思われる。実際のアクセスにはPEARが使えるならHTTP_Requestや、その関連のものを使うのがいいだろう(関連リンク)。ちなみに一番お手軽なのは、file_get_contentsだ。引数にURIを与えればXMLを取得できる。ただし利用できるように設定されていることが必要だ。file_get_contents(‘http://www.yahoo.co.jp’)等として、データを取得できるかどうかチェックしてみたほうがいい。またデータの取りこぼしが出る場合があるので、要注意だ。一番確実なのはfsockopenを使って、直接HTTPでお話しすることだ。そういうクラスを一つ書いておくのがいいだろう(今後、要望次第で時期を見て公開する予定)。もちろんHTTPの最低限の知識は必要になる。

(1)-2.取得したXMLをPHPで解釈する
XMLは構造化されているとはいえ、ただのテキストだ。PHPで扱いやすいようにするには、配列や構造体に格納してやるのがわかりやすい。そのためのライブラリや関数は以前に紹介した。
XMLを配列に読み込む
楽天APIを少し・・・(XMLを配列に)
XMLを配列に・・・これが最適解かも
上記の記事を参考にしていただきたい。

(1)-3.一日のアクセス上限回数を越えないよう制御する
ウェブはいつ誰がアクセスしてくるかわからない。もちろん、いつ誰がアクセスしても、うまく動いているように見せたい。「なぜかあのサイトは夕方以降はエラーが出る」なんてことにならないようにしなくてはいけない。例えば、単純にデータを毎回取得するとし、ウェブサービスの一日のアクセス上限回数が5000回と仮定して、一日6000回アクセスがあると、最後の1000回は、上限回数を超えているので必ずエラーになるのだ。これは避けなければいけない。
上限回数が決まっているのであれば、それを超えないようにすれば良い。一番簡単なのは「一度取得したデータを使いまわす」ことだ。いわゆるキャッシュだ。キャッシュの実現方法はさまざまだが二つ紹介すると・・・。

[a] cronで一定時間ごとにデータを取得しておき、ウェブからのアクセスの際はそのデータを参照する。
[b] PearのCacheを使い、前回アクセス時刻から一定時間以上経過していない場合は前回取得したデータを使いまわす。

前者の場合は、取得したデータをデータベースなどに格納しておけば、参照は容易だろう。後者の場合はPEARのCache_Liteがおすすめかな(以前の記事「キャッシュして軽いページを(Cache_Lite)」参照)。

これだけのことを知っていれば、メジャーなウェブサービスAPIの利用は難しくない。あとは(1)-2でPHPで扱いやすいように整形したデータを、いかに使うか、ということに尽きる。

ウェブサービスやAPIの利用に関しては、今後発展していくと思うので、もし不明な点があれば気軽にコメントで相談してもらっていいです(「スクリプトを書いてくれ」みたいなのはお金もらいますが、一緒に相談できるような人がいれば、勉強会を主催してもいいくらい・・・)。


守谷市(まちの情報ポータル) 無料アンケートレンタルjpForm.net