2010/03/25


twitter stream APIとは、twitterのステータス更新に対してキーワードでtrackしたり、あるグループ内に属するステータス更新をフィルタしたり出来るAPIなのだけど、実際にはchunkedなストリームが流れて来ているのであって、これを使ったWebアプリを作る際にはlong pollを使うのが良い。ただしクライアントサイドでjavascriptを処理する際に
  • サーバサイドでステータス更新をFIFOに溜め込む
  • クライアントからリクエストをブロック(long poll)しFIFOからステータスを送出する
  • クライアントサイドでlong pollを行い画面を更新する
  • 再度サーバへリクエストを投げる
を繰り返すのであれば、せっかくストリームなのにアプリサーバとの接続を切ってしまう事になる。出来ればクライアントからWebアプリもストリーミングとしたい。しかしながらサーバからのステータス更新を受け取るのであれば、JSON/JSONPなアクションが必要になる。
ここで使える!と思ったのがmultipart/mixedです。

multipart/mixedはboundaryで区切られたボディ部を繰り返し送出する際に用いられる仕組みで、実はXHR(XMLHttpRequest)はこのmultipart/mixedなレスポンスを受け取る事が出来る。
受け取るとは言っても、実際にはreadyStateが変化する...でしかないのだけど、これを扱うのに良いライブラリがある。
diggで使われているDUI.jsとStream.jsです。
digg's stream at master - GitHub

Alpha repository for DUI.Stream. When it stabilizes, it will be merged back into DUI

http://github.com/digg/stream
このstreamライブラリは内部でreadyStateが3になるのを待ち、タイマを張ってboundaryを監視するものです。つまり1 body部を扱う事になるので、使う際にはmime typeを指定する事になります。
var s = new DUI.Stream();
s.listen('text/html', function(payload) {
  ...
});
s.load('/push');
この仕組みを使って、twitterからのストリームをクライアントにmultipart/mixedで送出し、かつクライアントも接続を保持したままレスポンスを受け取るのです。接続を切らないので、ほぼタイムロス無しのリアルタイムと言って良いでしょう。
今日はこれをsinatraを使って実装してみました。出来れば一度動かしてみて下さい。これまでリクエストを繰り返し送っていたアプリケーションと比べてスムーズにデータを受け取っているのが分かって頂けるかと思います。
上記diggのライブラリを作業フォルダのstatic/jsフォルダ配下に置き、以下のファイル(fast-twitter-stream.rb)を作業フォルダ直下に置きます。
require 'rubygems'
require 'sinatra'
require 'json'  
require 'tweetstream'  
require 'pit'

# doesn't work on thin and webrick
set :server, 'mongrel'
set :public, File.dirname(__FILE__) + '/static'

config = Pit.get("twitter.com", :require => {
    "username" => "your username in twitter",
    "password" => "your password in twitter"
})

get '/' do
  <<HTML
  <html> <head>
    <title>Server Push</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>
    <script type="text/javascript" src="/js/DUI.js"></script>
    <script type="text/javascript" src="/js/Stream.js"></script>
    <script type="text/javascript">
    $(function() {
      var s = new DUI.Stream();
      s.listen('text/javascript', function(payload) {
        var status = eval(payload);
        $('#content').append('<p>' + status.text + '</p>');
      });
      s.load('/push');
    });
    </script>
  </head>
  <body>
    <h1>Server Push</h1>
    <div id="content"></div>
  </body>
</html>
HTML
end

get '/push' do
  boundary = '|||'
  response['Content-Type'] = 'multipart/mixed; boundary="' + boundary + '"'

  MultipartResponse.new(boundary, 'text/javascript')
end

class MultipartResponse
  def initialize(boundary, content_type)
    @boundary = boundary
    @content_type = content_type
  end

  def each
    TweetStream::Client.new(config[:username], config[:password]).sample do |status|  
      yield "--#{@boundary}\nContent-Type: #{@content_type}\n(#{status.to_json})"
    end  
  end
end
元となったコードはyoupyさんの物を参考にさせて頂きました。

ブラウザから"http://localhost:4567/"にアクセスすると、もの凄い勢いでステータスが追加されていくのが分かるかと思います。しかも切断していないのでかなり高速です。
お試しの効果には個人差があります。

このmultipart/mixed、結構使えるなーと思いました。得にリアルタイムな画像ストリーミングとして送出したり、リアルタイム電光掲示板の様な物にも使えるかと思います。

今回のサンプルもgithubに置いてあります。
mattn's fast-twitter-stream at master - GitHub

fast twitter stream client using multipart/mixed.

http://github.com/mattn/fast-twitter-stream
よろしければご参考までに。

Posted at by



2010/03/24


もう既出感丸出しですが...
リロード如きでソケットサーバとか使うの嫌だったのでWindows API使った。

function! s:UpdateBrowser()
  python<<EOM
import win32gui,win32api,win32con
hwnd = win32gui.FindWindow("MozillaUIWindowClass", None)
hwnd = win32gui.FindWindowEx(hwnd, 0, "MozillaWindowClass", None)
win32gui.SendMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_F5, 0)
EOM
endfunction

function! s:StartEditing()
  augroup HtmlEditing
    au!
    autocmd BufWritePost,FileWritePost *.html call s:UpdateBrowser()
  augroup END
  exec "!start c:/progra~1/mozill~1/firefox.exe " . expand('%:p')
endfunction

function! s:StopEditing()
  augroup HtmlEditing
    au!
  augroup END
endfunction

command! HtmlStartEditing call s:StartEditing()
command! HtmlStopEditing call s:StopEditing()
Windowsで、しかもFirefoxでしか動かない。「:HtmlStartEditing」で開始、「:HtmlStopEditing」で停止です。ファイルが更新される度にブラウザ(Firefox)にF5を送信します。Firefoxのパスとか適当に...
書いて5分程使ってみたけど、まぁまぁな感じ。いつかもうちょっと弄って使うかも。

同じ方法でchromeも出来るかもしれない。
Posted at by




スクリプトを書いたりしてる人にとってはkarmaなんかの評価値は励みになったりもします。
#!/usr/bin/env perl
use strict;
use warnings;
use URI;
use LWP::UserAgent;
use Web::Scraper;

my $user_id = shift || '103';

local $Web::Scraper::UserAgent = LWP::UserAgent->new;
$Web::Scraper::UserAgent->env_proxy;

my $account_url = "http://www.vim.org/account/profile.php?user_id=$user_id";
my $s1      = scraper {
  process '//td/a[contains(@href,"/scripts/script")]', 'karmas[]' => sub {
    my $e1     = shift;
    my $script_url = URI->new_abs( $e1->attr('href'), $account_url );
    my $s2     = scraper {
      process '//title',                         'name'  => 'TEXT';
      process '//td[contains(text(),"Rating")]', 'karma' => sub {
        my $e2 = shift;
        if ( $e2->as_text =~ /Rating ([^\s]*), Downloaded by ([^\s]*)/ )
        {
          return { rating => $1, downloaded => $2 };
        }
      };
    };
    my $staff = $s2->scrape($script_url);
    $staff->{url} = $script_url;
    $staff;
  };
  result 'karmas';
};
my @r = @{ $s1->scrape( URI->new($account_url) ) };

use YAML::Syck;
warn Dump @r;

# vim:set et:

---
karma: 
  downloaded: 25932
  rating: 1355/552
name: "calendar.vim - Calendar : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=52
---
karma: 
  downloaded: 783
  rating: 9/7
name: "which.vim - This is a unix like \"Which\" function. : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=139
---
karma: 
  downloaded: 1054
  rating: 9/10
name: "XpMenu - Make vim use WinXP style menu. : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=928
---
karma: 
  downloaded: 1884
  rating: 13/13
name: "VS like Class Completion - This script can complete member of cpp or java like Visual Studio. : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=675
---
karma: 
  downloaded: 943
  rating: 16/9
name: "ftplugin for Calendar - This is a ftplugin for Calenar. : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=683
---
karma: 
  downloaded: 1871
  rating: 92/37
name: "VimTweak - The tweaking dll for GVim.exe. : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=687
---
karma: 
  downloaded: 980
  rating: -1/1
name: "VimSpeak - Speak selected text with MS Agent : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=692
---
karma: 
  downloaded: 960
  rating: 11/4
name: "FTP Completion - complete files in command line for ftp. : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=963
---
karma: 
  downloaded: 183
  rating: 2/2
name: "Pit Configuration - pit configuration library for vim : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=2404
---
karma: 
  downloaded: 1282
  rating: 81/24
name: "Gist.vim - vimscript for gist : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=2423
---
karma: 
  downloaded: 805
  rating: 82/29
name: "GoogleReader.vim - vimscript for googlereader : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=2678
---
karma: 
  downloaded: 252
  rating: 20/5
name: "FastLadder.vim - vimscript for fastladder : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=2683
---
karma: 
  downloaded: 78
  rating: 0/0
name: "GoogleSuggest Complete - complete function using google suggest API. perhaps, you should input japanese w : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=2948
---
karma: 
  downloaded: 833
  rating: 101/29
name: "ZenCoding.vim - vim plugins for HTML and CSS hi-speed coding. : vim online"
url: !!perl/scalar:URI::http http://www.vim.org/scripts/script.php?script_id=2981
ダメダメなスクリプトとか、もう消した方がいいかな...とか思った。
Posted at by