最近、更新してないなーと思ったので、いつもなら記事にしない程度(というか段階)の内容。
いま、蔵書管理のためのWebサービス(SocialtunesやLibraryThingみたいなやつだ)を試作してて、書誌情報をどこから取ろうかと考えた結果、amazonを主に、NDL(国立国会図書館)を補助として使うことに決めた。
NDLだけでは最新の図書の書誌情報が得られず、amazonには古い図書の情報が少ないというのが主な理由だ(ほかにもNDLにはシリーズやNDCなんかの情報があったり、書誌情報の信頼性・正確性が高いというのもある)。
もちろんWebcat/Webcat Plusも考えたが、APIとして公開しているものがなく、HTMLからの抽出は、処理の重さや信頼性、あとマナー的にもどうなのということで断念。はやくAPI作ってくださいな・・・。
WorldCatやLCも視野に入れたけど、労力の割にそれほどメリットもないので、今回は無視。NDLがWorldCatに情報提供してれば使ったかもなのになあ。
で、amazonのAPI(Amazon Associates Web Service)は超有名なだけあって、簡単に使えた。
PHPのサンプル探してたら、こちらのページがわかりやすかった。
- 週末プログラマのメモ帳:PHPでAmazon E-Commerce Service (ECS) 2
※こちらではクエリ文字列をrawurlencodeでエンコードしているが、うちの環境では必要なかった。
で、今度はNDLがやってる電子リソースポータル「PORTA」(今回はNDLの所蔵データベースしか使わないが)のAPIを使ってみた。※APIについてはPORTAの「このサイトについて」から「外部提供インタフェースについて」で見られる。なんでパーマリンクがないんじゃあ!
なんかSRU(Seach/Retrieval via URL;LCの作った検索プロトコル。URLでリクエストしてXMLでレスポンスがくる)はともかくSRW(SRUのリクエストがSOAP仕様のXMLになったやつ)の情報があまりにも少なくて、なんでSRUを採用しないのかちょっと理解に苦しむところではあるが、まあそれしかないんだから使うしか仕方ない。
で、以下のコードで、一応結果のオブジェクトは返ってくる。
<?php
$client = new SoapClient("http://api.porta.ndl.go.jp/servicedp/services/SRWDp?wsdl");
$q = '(title = architecture) AND (dpid ANY "zomoku zassaku okayama")';
$req = array('version' => '1.1', 'query' => $q,
'startRecord' => '1', 'maximumRecords' => '200',
'recordPacking' => 'string', 'recordSchema' => '',
'sortKeys' => 'title,,1');
$result = $client->searchRetrieveOperation($req);
print_r($result);
?>
しかし、返ってきたデータを見てみると、肝心の書誌情報が入っているrecordDataオブジェクトの中身がなぜか空。ヒット数とかはPORTAのWebで検索したときと同じなので、いいとこまでいってるとは思うんだけどなあ。
ちなみに渡すパラメータが足りないとエラー(java.lang.NullPointerException)が返ってくる。省略可能なのもあるし、仕様書で「設定されない場合」とかあるやつでも省略不能っぽいのもある。
ところで、このAPI、公開されてそこそこ日が経つけど、誰か使ってるんやろか。情報すくないんだよなあ。
ちなみに、今回、開発はWindows上でxamppを走らせてやってます。xamppはApache、PHP5、MySQLやらが一気に使えて(しかもPortable)たいへん便利。ただ、ドライブのルートに配置しないといかんという制限があるので、仮想ドライブを指定するコマンドを書いた、下記のバッチファイルを作ってxammpディレクトリと同じディレクトリに置いて、開発時に実行してます。
rem connect current directory to x: drivesubst x: "%cd%"exit
追記(2008/12/17)
さっき、この追記を書き終えたところで、さあ更新しようと思って、フォームからフォーカス外すために適当なとこクリックしたら、ちょうどそこがリンクになってて、追記分まるまる消えましたよ・・・。もうやる気ないんで、雑な書き方で。箇条書きって楽だなあ。
- PHPが原因じゃないかとC#で試すもWeb参照でエラー。なんでや。
- soapUIを発見。試してみるとrecordDataにデータ入ってる!
- どうもrecordDataは<!CDATA[[]]>に囲まれて返ってくる模様。
- PHPのSoapClientがレスポンスXMLを配列にする時に落ちてるんかな(参考:xml_parse_into_structを使うと、xmlの中のCDATAが消える -OKWave)。
- ということは、これが落ちないようにするか、配列にする前のXMLをそのまま取得できればOK。
- 後者ならsimplexml_load_stringでLIBXML_NOCDATAオプション指定で配列に変換。
まだ最後に書いた解決策のどちらも見つけられてないんですが、また、進展があれば、書きます。
新しく記事起こした場合はこの記事にトラックバックしときます。
追記(2008/12/27)
進展があったのでご報告。ようやく書誌情報にアクセス出来ました。
まず、前の追記に書いていた、配列にする前のXMLを取得する方法がわかった。
SoapClient->__getResponseで最後のレスポンスを文字列で取得できる。てっきりこれも配列で返ってくると思ってたよ。
上のコードの例だと、$client->searchRetrieveOperation($req);のあとに、
$xmlstr = $client->__getRenponse();
で、レスポンスXMLを文字列として取得できる。
こいつをsimplexml_load_stringでCDATAをテキストノードに展開しつつ、配列に変換する。(参考:SimpleXMLでCDATAを取得したいときはLIBXML_NOCDATAを使う - F.Ko-Jiの「一秒後は未来」)
$r = simplexml_load_string($xmlstr, 'SimpleXMLElement', LIBXML_NOCDATA);
これをprint_rで見てみると、
SimpleXMLElement Object ()
あれ?パースエラーかなーなどといろいろ調べてみると、パース自体はできてるけど、ルート要素と名前空間が違う(あるいは名前空間が指定されてる)要素は、SimpleXMLElement->children()で名前空間のURIを指定してアクセスしないといかんらしい。(参考:.☆★ ステレオタイプラボ ★☆. [php]simplexml_load_fileでうまくパース出来ない、なんて事はない。& 正解とお手軽方法)
名前空間を指定する:を別の文字に置換しちゃうという手も魅力的だけど、まあ正攻法でやってみる。
先ほどの$xmlstrをprint_rで見てみると、こんな構成になってる。
(レスポンスされたデータ本体)
よって以下のコードでsearchretrieveresponseにアクセスする。
$srr = $r->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('http://www.loc.gov/zing/srw/');
これですべてのレコードにアクセスできるが、個々の書誌情報がはいってるrecordData要素には、書誌情報のXMLがCDATAとして入ってる。このままだとアクセスしにくいので、これも配列に変換する。例では1つめのレコードの書誌情報をrdに入れている。今回はLIBXML_NOCDATAは(たぶん)いらない。
$rd = simplexml_load_string($srr->searchRetrieveResponse->records->record[0]->recordData[0]);
で、さらにこの中身はルート要素と名前空間が違うので・・・
$dc = $rd->children('http://purl.org/dc/elements/1.1/');
で、ようやく書誌情報にまともにアクセスできます。めんどくせー!
で、上記の方法で取得してみたデータのサンプル。(クエリは$q = '(isbn = 4000074628) AND (dpid ANY "zomoku")';に変えてる)
SimpleXMLElement Object(
[title] => クマムシ?! : 小さな怪物
[creator] => 鈴木忠‖著
[description] => Array
(
[0] => 本体価格 : 1300円
[1] => シリーズよみ : イワナミ カガク ライブラリー ; 122
[2] => シリーズ : 岩波科学ライブラリー ; 122
[3] => 形態 : 112p ; 19cm
[4] => 出版地 : 東京
)
[publisher] => 岩波書店
)
ほとんどdescriptionにフィールド名とデータをまとめていれちゃってるという、なめてんのかってデータですね(スキーマがdcだからか?また追って調べます。今日はもう疲れた。)。