トップ «前の日記(2012-01-27) 最新 次の日記(2012-01-29)» 編集

Public Diary


2012-01-28

[プログラミング] TwitterのツイートをtDiaryにポストするスクリプト (続き)

昨日tweet2file_20120127をベースに、今日は細々とした処理を追加していきます。

ツイートの装飾

昨日の段階では、ファイルへのツイートの書き込み時、以下のような書き込みを行うようにしています。

tweets.each do |t|
  fh.puts "=" * 70
  fh.puts "* created_at: " + t.created_at.to_s
  fh.puts "* id: "         + t.id.to_s
  fh.puts t.text
end

これを、もう少しHTML的な出力にします。とりあえず、

<li><a href="パーマリンク">時刻</a> ツイート内容 <img src="rt.png" width="24" height="24" alt="発言者への返信" /></li>

という表記にし、加えてツイート本文中に @ があれば、そのユーザーのホーム画面へのリンクを張るくらいのことはしてみましょうか。

Twitterでは、各ツイートに「パーマリンク」と呼ばれるリンクが付けられていて、https://twitter.com/$screen_name/status/$id といったものになっています(一般的なシェル系の表記を用いています)。従って、<a>~</a> の部分については、

 line = "<a href=\"https://twitter.com/#{$screen_name}/status/#{t.id}\">#{t.created_at.strftime("%H:%m")}</a>"

でいけそうです。

続いて、Ruby の正規表現を用いて、@??? という文字列を <a href="https://twitter.com/???">@???</a> に置き換えます。ツイッターのアカウント名は、半角英数字+アンダースコアのみ利用可能で、文字数は1文字~15文字ということなので、否定の先読みを使いつつ、

text = t.text
line += text.gsub(/@([0-9A-Za-z_]{1,15})(?![0-9A-Za-z_])/) do
  url = "https://twitter.com/#{$1}"
  "<a href=\"#{url}\">#{$1}</a>"
end

といったところでしょうか。なんだか text.gsub() をしたあと text 自身が置換前のテキストを持っているのはちょっと気持ち悪いですかね。…と思ったら、gsub!() なんてメソッドがあるですか…うーむ。なお、最近は https://twitter.com/wassy といったように #! が入ることも多いと思いますが、Twitterのプロフィール表示では #! のないURLが表示されますので、こちらを使うことにします。

最後に、このツイートが過去のツイートへのリプライ等の場合に、参考リンクを付ける処理を行います。

if t.in_reply_to_status_id > 0 then
  link = "https://twitter.com/#{t.in_reply_to_screen_name}/status/#{t.in_reply_to_status_id_str}"
  line += "<a href=\"#{link}\"><img src=\"icons/rt.png\" alt=\"#{t.in_reply_to_screen_name}へのリプライ\" border=\"0\" /></a>
}

line += "</li>"

ところで、ツイート中に含まれるURLには自動的にリンクを張りたいものです。 これを処理するのに、URIライブラリが使えそうです。

require 'uri'

uri_reg = URI.regexp(%w[http https])
text.gsub!(uri_reg) { %Q{<a href="#{$&}">#{$&}</a>} }

ツイート中に含まれるリンクは、最近は t.co を使って省略されていることがあります。さらに、URLを展開するとそれはツイートに含まれる画像ファイルだったりすることもあります。できればこれらを単なるリンクとは区別して処理したいものです。

ツイートの装飾(クラスの拡張)

実は Twitter API では、user_timeline メソッドに対して include_entities オプションを渡すと entities として短縮URLや画像の情報が返されます。実験してみると、

require 'rubygems'
require 'twitter'
require 'pp'

ts = Twitter.user_timeline("wassy", {:include_entities=>true})
pp ts.first

という簡単なプログラムで

#<Twitter::Status:0x7f62bfa99998
 @attrs=
  { ...
   "entities"=>
    {"urls"=>
      [{"expanded_url"=>"http://bit.ly/...",
        "url"=>"http://t.co/Vx.....",
        "indices"=>[30, 50],
        "display_url"=>"bit.ly/..."}],
     "hashtags"=>[],
 ...

と、確かに entities が得られています。が、どうやらentitiesに対してはアクセサが用意されていないようで、クラスの外部からこの情報にアクセスできそうにないという問題点が出てきました。

こういうとき、Ruby ではさくっと既存クラスを拡張できるようで、今回は(やや禁じ手ですが)インスタンス変数に対する読み込み専用のアクセサを用意し、そこから情報を引き出すことにします。

module Twitter
  class Status
    attr_reader :attrs
  end
end

この5行を追加するだけで、内部の変数であった attrs にアクセスすることができるようになります。試しに、

 twts = Twitter.user_timeline("wassy", {:include_entities=>true})
 pp twts.first.attrs["entities"]["urls"]

とすると、本来アクセスできなかった entities => urls => expanded_url などにアクセスできるようになっています。あぁこういうライブラリの拡張のしやすさはRubyならでは、かも知れませんね。

以上を踏まえ、各ツイートの entities に対して

  • media に含まれるURLについてはサムネイルを取り出し
  • urls に含まれるURLは展開

を行い、その上で上記2件に該当しないURLについては通常のリンクを張るという処理を行うことにしました。現段階で、

といったマークアップができるようになってきました。もう一息です。これに若干修正を加えたものを tweet2file_20120128 として保存してあります。明日に続きます。


1980|03|
1986|04|
1998|04|
2002|01|11|
2003|03|04|05|07|08|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|02|03|04|06|07|08|11|12|
2008|01|02|03|04|06|07|08|09|10|
2009|01|12|
2011|05|10|11|
2012|01|02|10|