‘Ruby’ カテゴリーのアーカイブ

TwitterのUser Streams APIを試してみた

2010年11月25日 木曜日

こんばんは。最近、積極的にTwitterのストリーミングAPIを利用している(と思われる)サービスが増えてきましたね。何やらおもしろしそうなので少し触ってみる事に。

Streaming APIとは

The Twitter Streaming API allows high-throughput near-realtime access to various subsets of public and protected Twitter data.
http://dev.twitter.com/pages/streaming_api

  • Streaming API
  • User Streams
  • Site Streams (現在BETA)

ストリーミング系のAPIには3種類あって、それぞれ用途が分かれています。詳しくは上記リンク先を^^ 本当はSite Streamsを使いたいのだけど現在BETA版で申請が必要で、申請したけどなかなかレスがこないこともあり、今回はUser Streamsを試しました。

User Streamsとは

User Streams provides real-time updates of all data needed to update a desktop application display.
http://dev.twitter.com/pages/user_streams
User Streamsは、デスクトップアプリケーションの表示を更新するのに必要なすべてのデータをリアルタイムで提供してくれるAPIです。

すでにEchofonやTweetDeckではこのAPIに対応していて、とくにフォローが多い人はタイムラインが凄い勢いで流れていると思います。また、このAPIに対応することで、リプライの受信やメッセージの受信、ふぁぼられ、もちろんリツイートをリアルタイムに捕えることができます
以下、サンプルコードです。自分のツイートへのふぁぼりとリツイート、また自分への言及をリアルタイムに表示します。

お試し起動

以下のコマンドで。停止はCtrl+C。

1
# ruby user_timeline.rb
お試し実行結果

ソースコード(user_timeline.rb)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# -*- coding: utf-8 -*-
 
require 'rubygems'
require 'net/https'
require 'oauth'
require 'cgi'
require 'json'
 
CONSUMER_KEY = "XXXXXXXXXXXXX"
CONSUMER_SECRET = "XXXXXXXXXXXXX"
ACCESS_TOKEN        = "XXXXXXXXXXXXX"
ACCESS_TOKEN_SECRET = "XXXXXXXXXXXXX"
SCREEN_NAME = "yanaoki"
 
consumer = OAuth::Consumer.new( CONSUMER_KEY, CONSUMER_SECRET, :site => 'http://twitter.com') 
access_token = OAuth::AccessToken.new( consumer, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
 
uri = URI.parse('https://userstream.twitter.com/2/user.json')
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
https.ca_file = '/path/to/ca.cert' # path to certificate
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
https.verify_depth = 5
https.start do |https|
  request = Net::HTTP::Get.new(uri.request_uri)
  request.oauth!(https, consumer, access_token)
  buf = ""
  https.request(request) do |response|
    response.read_body do |chunk|
      buf << chunk
      while (line = buf[/.+?(\r\n)+/m]) != nil
        begin
          buf.sub!(line,"")
          line.strip!
          status = JSON.parse(line)
          # mentions
          if status["entities"] and status["entities"]["user_mentions"]
            if status["entities"]["user_mentions"].select{|u| u['screen_name'] == SCREEN_NAME }.size > 0 
              puts status['text']
              puts "mention from #{status['user']['screen_name']}(#{status['user']['id']})"
            end
          end
          # retweets
          if status['retweeted_status'] and status['retweeted_status']['user']['screen_name'] == SCREEN_NAME
            puts "#{status['retweeted_status']['text']}"
            puts "retweeted by #{status['user']['screen_name']}(#{status['user']['id']})"
          end
          # favorites
          if status['event'] == 'favorite' and status['source']['screen_name'] != SCREEN_NAME
            puts "#{status['target_object']['text']}"
            puts "favorited by #{status['source']['screen_name']}(#{status['source']['id']})"
          end
        rescue
          # TODO
        end
      end
    end
  end
end

RubyWorld Conference 2010に参加してきた

2010年9月7日 火曜日

こんばんは。

昨日から二日間(2010年9月6日~2010年9月7日)、「RubyWorld Conference 2010」に参加してきました。

http://www.rubyworld-conf.org/

( Twitterハッシュタグ: #RWC2010 )

参加者の他県率は8割くらい(レセプション時の感覚)で、スーツ率はなんと9割くらいでした。参加者は、6日 415名、7日 693名とのことです。

発表内容からメモした言葉をちょっとだけ紹介し、後は写真をアップして寝ることにします。

基調講演:まつもとゆきひろ氏(@yukihiro_matz)『持続可能なRuby』

1993 ruby開発開始 ユーザまつもとさん1人

1995 ruby公開

2009 Rubyユーザ100万人(ガートナ調査結果)

2013 Rubyユーザ400万人(ガートナー予想)

すごいユーザ数の推移ですね。400万人予想はRails全盛期の予想らしく実際にはそこまでいかないらしいけど。

角谷信太郎氏(@kakutani)『ふつうのシステム開発~Rubyを活用した受託開発をアジャイルにするためのパターンの紹介』

「時を超えた建設の道: クリストファー アレグザンダー著」からの言葉

生きている花をつくろうとすれば、ピンセットで細胞を一つ一つ物理的に組み立てるのではなく種から育てるのであろう。

これは、ささりました。

何か事を成すときの考え方として、どんなケースでも当てはまる言葉ですよね。

そしてきっと誰でも思い当たる事があるでしょう。

Stevie Clifton氏(Animoto Productions)『How Ruby Enables Startup Success』

Stevie氏はANIMOTO(http://animoto.com/)という動画サービスのCTO。こういうサクセスストーリーを聞くのは楽しいです。とてつもない苦労をされてるんだろうけど、それにも増してサービスの提供を楽しんでいることが言葉の壁を超えて伝わってくる。クローゼット内のサーバで始めたサービス→EC2で30インスタンス規模まで拡大。Facebookアプリによって30台から3,000台までスケールアウト。そしてアーキテクチャの再構成。すべてのフェーズにてRubyとその周辺のプロダクトを採用したとのことです。

まさに海外でのRuby&Railsの成功例ですね。そしてANIMOTOのデモすげーかっこよかった。

-

全て1日目のレポートになってしまいましたが。2日目も楽天・三木谷社長の講演など内容は濃かったですよ。

-

以下写真。

会場:島根県立産業交流会館「くにびきメッセ」

-

ノベルティのエコ箸と、売店で売ってるRuby on 松江ラーメン(2食分)

ラーメンの箱の上部に書いてあるのはMatzのサインだろうか…。

-

Aホール:まつもとさん講演中

-

1日目昼食お弁当:ボリュームたっぷり

-

B会場

-

1日目:多目的ホール。夜のレセプションの開始直前

-

2日目昼食お弁当

-

なんか食べ物の写真ばかりで申し訳ない(汗)

以上です。何を言いたいかというと、来年も参加したいなーということです。

EC-CUBEの商品登録CSVデータを生成するRubyスクリプト

2010年8月31日 火曜日

こんばんは。

EC-CUBE用のテストデータを登録するためのCSVファイルを作る必要があって、簡単なスクリプト書いたので公開しておきます。

今回は、楽天ランキングAPIのデータを使ってテストデータのCSVを作成し、EC-CUBEのテストデータとして登録する手順を紹介します。
間違ってもそのまま公開しないように!

EC-CUBEのWeb管理ツールで手動でカテゴリ登録する

まず、EC-CUBEのWeb管理ツールでカテゴリを一つたとえば「ゴルフ」を手動で登録します。
登録したカテゴリのIDを管理ツール上で確認します。
ここでは「ゴルフ」のカテゴリIDが「5」だったとします。

楽天ジャンルIDの取得する

楽天のジャンル一覧で「ゴルフ」のジャンルIDを確認します。
ゴルフのジャンルIDは「101077」でした。
楽天ジャンルID(genreId)検索

必要なコードを準備

Simple JSON parser & builder
以下のサイトからJSON解析スクリプトをダウンロードし「jsonparser.rb」とします。ここにも置いておきます。
http://rubyforge.org/snippet/detail.php?type=snippet&id=148
httpclient
画像ファイルを取得するのにhttpclientを利用しているのでインストールします。

$ sudo gem install httpclient

以下のrubyスクリプトをコピーして適当な名前で保存します。
ここでは「itemgen.rb」とします。
ざっくしとした説明です。

  • setupメソッドでIDで設定を行う
  • csv_formatメソッドでCSV形式を定義する
  • rewriteメソッドでCSVの内容を定義する
$KCODE='u'
require 'rubygems'
require 'httpclient'
require 'open-uri'
require 'kconv'
require 'jsonparser.rb'
require "fileutils"
require 'jcode'
 
# 初期設定
def setup
  $developer_id = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'  # APIのためのデベロッパID
  $img_dir = 'img'                         # 画像保存ディレクトリ名
  $item_csv_filename = "item.csv"   # 商品CSVファイル名
  $genre_id = ARGV[0]                  # 楽天ジャンルID
  $category_id = ARGV[1]             # カテゴリID
  $max_page = ARGV[2]               # 取得ページ数(1ページ30商品で10ページまであるらしい)
end
 
# CVSフォーマット(HashをsortしてArray返してるので注意)
def csv_format
  {
    0 =&gt;  "商品ID",
    1 =&gt; "商品規格ID",
    2 =&gt; "規格名1",
    3 =&gt; "規格名2",
    4 =&gt; "商品名(必須)",
    5 =&gt; "公開フラグ(1:公開 2:非公開)(必須)",
    6 =&gt; "商品ステータス(必須)",
    7 =&gt; "商品コード",
    8 =&gt; "通常価格 ",
    9 =&gt; "販売価格(必須)",
    10 =&gt;"在庫数",
    11 =&gt;"送料",
    12 =&gt;"ポイント付与率(必須)",
    13 =&gt;"購入制限",
    14 =&gt;"メーカーURL",
    15 =&gt;"検索ワード",
    16 =&gt;"備考欄(SHOP専用)",
    17 =&gt;"一覧-メインコメント(必須)",
    18 =&gt;"一覧-メイン画像(必須)",
    19 =&gt;"メインコメント(必須)",
    20 =&gt;"メイン画像(必須)",
    21 =&gt;"メイン拡大画像",
    22 =&gt;"カラー比較画像",
    23 =&gt;"商品詳細ファイル",
    24 =&gt;"詳細-サブタイトル(1)",
    25 =&gt;"詳細-サブコメント(1)",
    26 =&gt;"詳細-サブ画像(1)",
    27 =&gt;"詳細-サブ拡大画像(1)",
    28 =&gt;"詳細-サブタイトル(2)",
    29 =&gt;"詳細-サブコメント(2)",
    30 =&gt;"詳細-サブ画像(2)",
    31 =&gt;"詳細-サブ拡大画像(2)",
    32 =&gt;"詳細-サブタイトル(3)",
    33 =&gt;"詳細-サブコメント(3)",
    34 =&gt;"詳細-サブ画像(3)",
    35 =&gt;"詳細-サブ拡大画像(3)",
    36 =&gt;"詳細-サブタイトル(4)",
    37 =&gt;"詳細-サブコメント(4)",
    38 =&gt;"詳細-サブ画像(4)",
    39 =&gt;"詳細-サブ拡大画像(4)",
    40 =&gt;"詳細-サブタイトル(5)",
    41 =&gt;"詳細-サブコメント(5)",
    42 =&gt;"詳細-サブ画像(5)",
    43 =&gt;"詳細-サブ拡大画像(5)",
    44 =&gt;"発送日目安",
    45 =&gt;"おすすめ商品(1)",
    46 =&gt;"詳細-サブコメント(1)",
    47 =&gt;"おすすめ商品(2)",
    48 =&gt;"詳細-サブコメント(2)",
    49 =&gt;"おすすめ商品(3)",
    50 =&gt;"詳細-サブコメント(3)",
    51 =&gt;"おすすめ商品(4)",
    52 =&gt;"詳細-サブコメント(4)",
    53 =&gt;"おすすめ商品(5)",
    54 =&gt;"詳細-サブコメント(5)",
    55 =&gt;"おすすめ商品(6)",
    56 =&gt;"詳細-サブコメント(6)",
    57 =&gt;"商品カテゴリ(必須)"
  }.sort_by{|k,v|k}
end
 
# CSVの値を定義(フォーマットの列番号に対応。xにはAPIで取得したitemが入る)
def rewrite
  {
    4  =&gt;lambda{|x| cut_off(x["itemName"].to_s.toutf8.gsub(/【.[^【]*/, '').gsub("楽天",''),45)}, #【】は取る
    5  =&gt;lambda{|x| "1"},
    6  =&gt;lambda{|x| "0"},
    7  =&gt;lambda{|x| x["itemCode"]},
    9  =&gt;lambda{|x| x["itemPrice"]},
    10 =&gt;lambda{|x| "20"},
    12 =&gt;lambda{|x| x["pointRate"]},
    17 =&gt;lambda{|x| s = cut_off(x["itemCaption"].to_s.toutf8.gsub("楽天",'').gsub('。','。'),50) ; s == "" ? "-" : s},
    18 =&gt;lambda{|x| image_get(x["smallImageUrl"],x["itemCode"] + "_s.gif")},
    19 =&gt;lambda{|x| s = x["itemCaption"].to_s.toutf8.gsub("楽天",'').gsub('。','。') ; s == "" ? "-" : s},
    20 =&gt;lambda{|x| image_get(x["mediumImageUrl"],x["itemCode"] + "_m.gif")},
    57 =&gt;lambda{|x| $category_id}
  }
end
 
def image_get(image_url,save_file_name)
  hc = HTTPClient.new
  f = File.open("#{$img_dir}/" + save_file_name, "wb")
  f.print(hc.get_content(image_url))
  f.close
  save_file_name
end
 
def api_call(uri)
  json_string = ''
  open(uri){|f| json_string = f.read }
  parser = JsonParser.new
  parser.parse(json_string)
end
 
def cut_off(text,len)
  if text != nil
    text.jlength &lt; len ? text : text.scan(/^.{#{len}}/m)[0] + "…"
  else
    ''
  end
end
 
setup
# ファイルを掃除しとく
FileUtils.remove_entry($img_dir,true)
FileUtils.mkdir($img_dir)
FileUtils.rm($item_csv_filename, {:force=&gt;true})
File.open($item_csv_filename,'w') do |f|
  # ヘッダ行を追加
  f.puts(csv_format.collect{|col| "\"#{col[1]}\""}.join(','))
  (1..$max_page.to_i).each do |page|
    items = []
    ranking_api_url = "http://api.rakuten.co.jp/rws/3.0/json?developerId=#{$developer_id}&amp;operation=ItemRanking&amp;version=2010-08-05&amp;genreId=#{$genre_id}&amp;page=#{page}"
    # APIを呼び出して戻り値のアイテムをもとにCSV行を作成
    obj = api_call(ranking_api_url)
    exit unless obj["Body"]["ItemRanking"]
    rakuten_items = obj["Body"]["ItemRanking"]["Items"]["Item"]
    puts "ページ#{page} から #{rakuten_items.size} 件取得しました。"
    rakuten_items.each do |item|
      items &lt;&lt; csv_format.collect do |col|
        i=col[0]
        rewrite[i] ? rewrite[i].call(item) : ""
      end
    end
    # 出力
    items.each do |item|
      f.puts(item.collect{|col| "\"#{col.to_s.gsub(',','')}\""}.join(','))
    end
  end
end
実行する

以下のコマンドで実行します。
パラメータは「ジャンルID カテゴリID 取得ページ数」です。

$ ruby itemgen.rb 101077 5 3
  • item.csv
  • imgディレクトリ

が生成されます。

画像を配置する

先に画像を配置しておく必要があるので、imgディレクトリ内の全画像を、
「html/upload/save_image/」に配置します。

登録する

Web管理ツールからCSVファイルを登録します。

完了

以上です。いろいろと使えそうな気がしますね。