« ブログパーツ「モアイとばし」 | メイン | JSONP + リクルートWEBサービスのサンプル:プルダウンメニュー生成 »

エイビーロードWebサービスをRubyで試す このエントリーを含むはてなブックマーク BuzzurlにブックマークBuzzurlにブックマーク

フナミタカオです
リクルートWebサービスが公開されて10日ほど、たちましたが、皆様もう触られましたでしょうか?
マッシュアップアワード3のネタとしても、ぜひ、ためしてみてくださいね

今回は、海外旅行情報:エイビーロードWebサービスのサンプルコードをRubyでつくってみました。
Ruby1.8.xが前提ですが、FTPしてもらえれば、動くようになっていると思います。
さくらインターネットと、DreamHostで確認済みです。

00.jpg
実は、PHPでも、サンプルコードを書き始めていたのですが、簡単なスクリプトから、逸脱して、気がつくと、俺プチフレームワークを作り始めていました。
なれないげんごだと、力加減がわからず、つい作りこみすぎてしまったりするものです。
僕にとっては、一番のお気に入りのRubyで再実装してみました。
簡単なところから初めて、最終的には、クラス化や、Ruby On Railsのプラグイン化も目指しつつ、少しづく充実してゆく予定です。

今回は、キーワードで、ツアー一覧を検索するという簡単なものですが、XMLから、検索結果を取り出す方法や、ページングの処理、ERBを使ったテンプレート等参考にしていただけるとうれしいです。

機能概要

DreamHost上での動作サンプルさくらインターネット上での動作サンプル(←サンプルは外部サイトへのリンクとなります)
キーワードと、出発月のしてのみができるという、シンプルなつくりですが、最低限のページングもつけました。

XMLで呼び出す

Ruby1.8以上であれば、REXMLが標準添付されています。
エイビーロードWebサービスは、xmlだけでなく、json、jsonpフォーマットでも受け取れます。jsonのほうが、rubyのハッシュにするのは、やりやすいし、転送されるファイルも小さいので、できればjsonを選択したい。
ただ、jsonを使うとなると、未インストールの場合はインストールが必要になるので、ここは、いったんXMLをつかたサンプルを書いてみました

Webサービスにクエリを投げているのが、44~48行目付近の以下のコード
uri = "#{API}?#{query.to_q}" #ここに、リクエストのURLが入る
result = nil
# xml読み込み
open(uri) do |f| 
    obj = REXML::Document.new(f.read)  #f.readで一気に読み込んでオブジェクト化
    result = obj.elements['results']         #results要素を取り出した。
end
↑のようにXMLからresultのオブジェクトを生成した

各要素はelementsメソッドで取得できます。
result.elements['results_available'].text.to_i
results_availableにて、検索条件の全ヒット件数が戻る。
textをつけないと、要素タグが、ついたまま戻ってくるの注意
print result.elements['results_available'] は
 <results_available>18</results_available>
のように、要素名までかえす。
textメソッドを、いちいち書かなくてはいかないので、ちょっと面倒。

複数要素の取り出し

ツアーの検索結果には、複数のツアーが含まれる可能性がある。(もちろん0件になる場合もあるが) 各ツアーにを読み出すために、ループをまわすが記述は以下のようになる。
result.elements.each('tour') do |tour|
  tour.elements['id']
end
idはtour.elements['id']で取り出せるので、複数項目を
result.elements['tour'].each  do |tour|
     ......
end 
と書いても、動きそうだが、これはうまくいかない。
いづれにしても、REXMLのDomになっているので、単純なハッシュではない。ppでダンプしても、全体構造が見えなくて、ちょっと、わかりづらい。
このあたりのデータのハンドリングはjsonのほうが、断然、やりやすい。とはいえ、通常はクラスになっているのがよろしいので、XMLとJsonの差異は実行時のパフォーマンスで効いてくるかもしれない
XMLのほうが、閉じタグがあって、どうしても同じ情報量をあらわすときのバイト数が、Jsonよりも多くなる。

XPathも使える

ツアーの出発地名は
tour.elements['dept_city/name'].text 
とXPathを指定しても可能。

テンプレートはERBで

ロジックとデザインの分離のためにERBを利用しています。
71行目あたりからのヒアドキュメントが、テンプレートです。
今回はサンプルとして1ファイルに収めたかったので、テンプレートも一緒に書きましたが、本来は、別ファイルに書いていくべきだと思います。
テンプレートを別にすることで、テンプレートはDreamweaverでの編集とかがやりやすくなります。
Ruby on RailsもERBを使っていますが、かなり拡張されている。今回はRuby on railsのライブラリは使っていないので、3桁ごとに”,”を打つロジックは、別途メソッドを追加しました。(ただし、数値に限らず、右から3桁づつのところにカンマを挿入するだけです)
String#to_priceです。
class String
    def to_price
        temp = self.reverse
       # temp.scan(/.{3}/).join(',').reverse 
       #↑すみません、これはまちがいでした。これだと50000が000になってしまいます。
        temp.scan(/.?.?./).join(',').reverse
    end
end
文字列をリバースして、3文字づつの配列にし、カンマでジョインして、再度リバースしています。 今回は、スタイルシートは用意していませんが、最終的な画面デザインは、できるだけCSSに任せてゆくつもりです。

サーバーで動かしてみる

さくらのレンタルサーバーと、DreamHostで動作確認しました。
ただし、1行目のrubyパスは、環境によって異なるので、適宜変更してください。
さくらの場合
#!/usr/local/bin/ruby
DreamHostの場合
#!/usr/bin/ruby
ロリポップでは、動作時に500のエラーになります。(原因調査中ですがTelnetできないので、思案中、rubyのバージョンかなあ...)

インストール手順
  1. ファイルをダウンロード
  2. 1行目のRubyのバスの確認・必要なら修正
  3. 35行目のAPIキーの設定
  4. 以上の修正後、
  5. FTPでアップロード
  6. 実行権限の付与
  7. ブラウザから実行

全ソース

#!/usr/local/bin/ruby

$LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
require 'open-uri'
require 'erb'
require 'cgi'
require 'rexml/document'
require 'date'
require 'pp'

class Hash
    def to_q()
        query = []
        self.each do |key,value|
            query << "#{key}=#{ERB::Util.u(value.to_s)}"    
        end
        query_str = query.join("&") 
    end
end

class String
    def to_price
        temp = self.reverse
        temp.scan(/.?.?./).join(',').reverse
    end
end
                                
API = 'http://webservice.recruit.co.jp/ab-road/tour/v1/'
MY_API_KEY = 'guest'#<-ここに、自分のAPIキーを入力してください

cgi = CGI.new
query = cgi.params
query['key'] = MY_API_KEY #apiキーのセット
query['format'] = 'xml'  #formatをxmlに固定
query['count'] = 10
#query['ym'] = (Date.today >> 1).month if query['ym'] == 0
uri = "#{API}?#{query.to_q}"
result = nil
# xml読み込み
open(uri) do |f|
    obj = REXML::Document.new(f.read)
    result = obj.elements['results']
end

#ページングの計算
page = nil
page_query = query.dup
page_query.delete 'start'
page_query = page_query.to_q
if result.elements['results_available'] && result.elements['results_available'].text.to_i > 0
    page = {}
    page['current'] =  (result.elements['results_start'].text.to_i/query['count']).truncate
    page['first'] = 0 
    page['prev'] = page['current'] - 1  
    page['prev'] = page['first'] if page['prev'] < page['first']
    page['last'] = (result.elements['results_available'].text.to_i/query['count']).truncate
    page['next'] = page['current'] + 1
    page['next'] = page['last'] if page['next'] > page['last']
end

query.delete 'key' #キーは削除する


#以下テンプレート
#Ruby on Railsでも、おなじみのERBを利用しています。
script = <<EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title><%= query.to_q %></title> 
</head>
<body style="margin:10pt;font-size:0.8em">
    <p><a href="http://webservice.recruit.co.jp"><img src="http://webservice.recruit.co.jp/banner/abroad-s.gif" alt="エイビーロードWebサービス" width="135" height="17" border="0" title="エイビーロードWebサービス"></a></p>
    <form action="" method="get">
        フリーワード:<input type="Text" name="keyword" value="#{query['keyword']}"> 
        出発月: 
        <select name="ym">
            <%- d = Date.today  -%>
            <option value="" >指定なし</option>
            <%- 12.times do |i| -%>
                <%- d = d >> 1 -%>
                <%- if query['ym'].to_s == d.month.to_s -%>
                <option value="<%= d.month %>" selected="selected"><%= d.month %>月</option>
                <%- else -%>
                <option value="<%= d.month %>" ><%= d.month %>月</option>
                <%- end -%>
            <%- end -%>
        </select>
        <input type="submit">
    </form>
    <%- if result.elements['error'] -%>
        <ol>
        <%- result.elements.each('error') do |error| -%>
            <li style="color:red">エラー:<%= error.elements['message'].text %></li>
        <%- end -%>
        </ol>
    <%- elsif result.elements['results_available'].text.to_i == 0 %>
        <p>検索で一致するコースは見つかりませんでした。</p>
    <%- else -%>
        <p>検索結果:<%= result.elements['results_available'].text %>件(<%= result.elements['results_start'].text %>~<%= result.elements['results_start'].text.to_i+result.elements['results_returned'].text.to_i-1 %>件目)<br />  
        <%- if page -%>
            <a href="?#{page_query}&start=<%= page['first']*query['count']+1 %>" >&lt;&lt;先頭</a> 
            <a href="?#{page_query}&start=<%= page['prev'] *query['count']+1 %>" >&lt;前</a> 
            [ <%= page['current']+1 %> ] 
            <a href="?#{page_query}&start=<%= page['next'] *query['count']+1 %>" >次&gt;</a> 
            <a href="?#{page_query}&start=<%= page['last'] *query['count']+1 %>" >最後&gt;&gt;</a> 
        <%- end -%>
        </p> 
        <%- result.elements.each('tour') do |tour| -%>
        <div style="margin:10pt 0;">
        <%= tour.elements['dept_city/name'].text %>発:<%= tour.elements['term'].text %>日間 旅行代金:<%= tour.elements['price/min'].text.to_price %>円~<%= tour.elements['price/max'].text.to_price %>円<br/>
        <a href="<%= tour.elements['urls/pc'].text%>"><%= tour.elements['title'].text -%></a><br />
         訪問地:<%= tour.elements['city_summary'].text %><br />
         ブランド:<%= tour.elements['brand/name'].text %><br />
        </div>
        <%- end -%>
    <%- end -%>
    <div style="margin:10pt 0">
    <pre><%= "#{API}?key=[API KEY]&" << query.to_q %></pre>
    </div>
</body>
</html>
EOF

#書き出し
e = ERB.new(script,nil,'-')
print "Content-Type: text/html; charset=UTF-8\n\n"
e.run



今回のコーディングで参考にしたページ

逆引きRuby 日付と時刻:翌月等、何ヶ月かの月を求める方法
標準添付ライブラリ紹介 【第 10 回】 ERB
3桁ごとにカンマを入れる
AB-ROAD エイビーロード | APIリファレンス | リクルートWEBサービス

次回予告

都市、国マスタを使ったサンプルを作成してみる予定です。

トラックバック

この一覧は、次のエントリーを参照しています: エイビーロードWebサービスをRubyで試す:

» 最近のMTLブログ新着エントリー (8/3~21) 送信元 たたみラボ_blog
研究員の石橋利真です。こんにちは。 ひとつ前のエントリーでもお知らせしたとおり、僕らは4月から活動の場を「メディアテクノロジーラボ - MTL」に移し... [詳しくはこちら]

コメント (1)

Anonymous:

なぜか僕の環境だと

result.elements['results_available'].text
とするとエラーが出ますね。
rdeでルビーバージョン1.8.6なんでけど。

MA5

最近のコメント

Tag cloud