Tumblr::Stat

自分のpost/reblogの統計をちょっととってみようかと。項目としては、

  • 総 post/reblog 数 / 日毎推移
  • posted by: 誰の元ポストを reblog しているか
  • reblogged from: 誰の post/reblog を reblog しているか
  • reblogged by: 誰に reblog されているか
  • reblog/follower overlap: 。。。ちょっと説明しにくいが、自分の reblog した post を誰が reblog しているか

http://d.hatena.ne.jp/myhoney0079/20090104/p1の焼き直しだけど、骨格としては:

require "rubygems"
require "tumblr"
require "open-uri"
require "hpricot"

module Tumblr
  class Stats
    class Result
      def show (out = STDOUT)
        out << "=== result ===\n"
      end
    end
    def initialize (user, key)
      @user = user; @key = key
    end
    def get_posts(num)
      []
    end
    def get_stats(posts)
      return Result.new()
    end
    def run (num=-1)
      posts = get_posts(num)
      result = get_stats(posts)
      result.show
    end
  end # class Stats
end # module Tumblr

################################################################

def run
  user = 'myhoney0079'
  key = 'xxxxxxxx'
  num = 100

  (stats = Tumblr::Stats.new(user, key)).run(num)
end

run()

Tumblr::API 必須。key は dashboard のhtmlソース見て display_post_notes(id, key) を探す。

Tumblr::Stats が主クラス。run() を呼ぶと、get_posts() ですべての post (Tumblr::Data::Post)を取得し、それを get_stats(posts) に渡し、そこでごにょごにょする。

get_stats(n) では、posts.each し、それぞれの post の reblognote 見て reblog 履歴を取得し、posted_by, reblogged_from, reblogged_by などを解析し、ハッシュに入れる。その結果を Tumblr::Stats::Result オブジェクトにいれ、返す。そして表示。。。という流れ。

n で解析する post 数を指定。無指定あるいは負だと全 post。。。。その度ごとに全 post にアクセスして reblognote を取得していると負荷がかかるので、cache を実装予定。

get_posts()

    def get_posts(num)
      uri = "#{@user}.tumblr.com"
      posts = []
      Tumblr::API.read(uri) do |pager|
        l = pager.last_page    ## yet: now getting all posts...l = (num / 20).to_i ??
        l = 0                  ## for debug
	0.upto(l) do |i|
          print "getting post..(#{i+1}/#{l+1}pages)\n"
          pager.page(i).posts.each do |post|
            posts << post
          end
        end
      end
      return posts
    end

サンプルどおり、Tumblr::API.read()を使う。l (読み込みページ数)の計算は後ほど。

get_stats()

    def get_stats(posts)
      print "getting stats...\n"
      result = Result.new()

      i = 0; n = result.num_posts = posts.size
      posts.each do |post|
        print "..analysing post '#{post.postid}' (#{i+1} / #{n})\n"; i += 1
        note = Tumblr::Reblognote.new(post.postid, @key)
        result.posted_by[note.posted_by] += 1
        result.reblogged_from[note.reblogged_from] += 1 unless note.posted_by == @user
        note.reblogged_by.each do |foo|
          result.reblogged_by[foo] += 1
        end
      end
      return result
    end

渡された posts を each で回して、Tumblr::Reblognote オブジェクトを作り、解析。結果を Tumblr::Stat::Result オブジェクトに入れる。

Tumblr::Stat::Result

    class Result
      attr_accessor :posted_by, :reblogged_by, :reblogged_from
      attr_accessor :num_posts
      def initialize
        @posted_by = Hash.new(0)
        @reblogged_by = Hash.new(0)
        @reblogged_from = Hash.new(0)
        @num_posts = 0
      end
      def show (out = STDOUT)
        out << "=== result ===\n"
        p @posted_by, @reblogged_from, @reblogged_by
        
      end
    end

必要な情報の accessor を用意するだけ。show() は、sort するなりなんなり。

Tumblr::Reblognote

module Tumblr
  class Reblognote
    class Item
      attr_accessor :action, :actor, :from
      def initialize(action, actor, from)
        @action = action; @actor = actor; @from = from
      end
    end # class Item

    def initialize(id, user, key)
      @id = id; @user = user; @key = key
      @uri = "http://www.tumblr.com/dashboard/notes/#{id}/#{key}"
      @notes = []
      get_notes()
    end
    def posted_by
      return @notes[@notes.size-1].actor
    end
    def reblogged_from
      @notes.each do |item|
        print "   check rf: #{item.actor} #{item.action} from #{item.from}\n"
        if item.action == "reblogged" and item.actor == @user
          print "    ..u reblogged from #{item.from}\n"
          return item.from
        end
      end
      return nil
    end
    def reblogged_by
      ["a", "b", "c"]                ## to be implemented
    end
    private
    def get_notes
      print "get url.. #{@uri}\n"
      (省略)
      return @notes
    end
    def method_missing(msg, *arg)
      @notes.send(msg, *arg)
    end

  end # class Reblognote

この前突貫で作ったやつの焼き直し。クラスにしただけ。ただ initialize() で http access するようにしちゃったけど、値参照時まで遅延させたほうがよいかも。