Google OpenSocial API - mixiに期待しつつJavaScript Gadgetを試す JavaScript OpenSocial
2007-11-05
sandbox.orkut.com のアカウントが使えるようになったので、GoogleのOpenSocial APIを試してみた。OpenSocial Protocolについて詳細はこちらのサイトが詳しいようです
このブログに書いてある通り、OpenSocial Protocolは
- クライアント用のJavaScript API
- サーバ用のOpenSocial Data API
の2種類があって、サーバ側 OpenSocial Data APIの実装はまだ無いみたい。ということで今回試したのはJavaScriptクライアントの方。ドキュメントへ書いてある通り、Orkut上で試してみる。あとこのドキュメントを開いとく
OpenSocial API Developer's Guide
sandbox.orkut.comへ登録
http://code.google.com/support/opensocialsignup/ から登録、数時間でメールが届いた。普通のwww.orkut.comとデータの互換性はある、唯一アプリケーションがsandboxでしか有効にならない。
Google Gadgets Editor
便利なので Google Gadgets Editorを利用する。
http://code.google.com/apis/gadgets/docs/gs.html#Scratchpad で自分のiGoogleホームに追加しておくと便利かも。自分のサーバがあれば、そこにXMLを置くだけでもOK。
デフォルトのHello worldアプリで試してみた、右のhello.xmlのリンクURLをコピー。http://sandbox.orkut.com/MyApps.aspx を開いて「Add an application by URL:」へ張り付けて「add application」を実行。プロフィールを表示して「Hello, world!」が表示されればOK
OpenSocialAPIを叩く
まず試しということで「OpenSocial APIから自分(Owner)の情報を取ってプロフィールURLを見つける、そのURLをfooo.nameから検索して結果を画面に表示」をやってみた。
//自分の情報を取得 var req = opensocial.newDataRequest(); req.add(req.newFetchPersonRequest('OWNER'), 'owner'); req.send(onLoadFriends);
複数件の情報取得を一度のリクエストでまとめて取ることを想定しているらしい。他にも色々とある http://code.google.com/apis/opensocial/docs/javascript/reference/opensocial.DataRequest.html
で、結果をコールバック関数のonLoadFriendsで処理する。ちなみにOwnerなのにonLoadFriendsという名前なのはサンプルコードをコピッてるから:)
var owner = dataResponse.get('owner').getData();
var url = owner.getField(opensocial.Person.Field.PROFILE_URL);
指定したキー(owner)でPersonオブジェクトを取得、この仕様に従って情報を抜き出す、今回はプロファイルURLを取った。あとはいつも通りJavaScriptで書けばOK
OrkutのプロファイルURLからfooo.nameで検索、結果を表示。自分の持ってる他のページ一覧が表示されている。本当は「フレンドの〜」てのがSNSぽくて面白いと思ったけど、Orkutのフレンドがいないのでできなかった:(
やってみて特別難しいことは無い、このパーツも結局IFrameで動くので普段通りJavaScriptで作ればOK、Google Gadgets作った経験のある人はさらに楽なのかも。作ったパーツがMySpace/mixi/VOX?/Friendster なんかで汎用的に動くのは良い。ただ今現在だと「OrkutでGoogle gadgetsが動く」という状況と変わらないのであんまり面白くないな、まずはmixiの対応に期待かも。
最初 GoogleがMySpaceがFacebookが、という話を聞いてる時点では割とどうでも良かったけど、mixiが参加するなら日本としても面白くなりそう。
適当だけど一応書いたコードこれ ↓ 途中PROFILE_URLが相対パスで「死ね」と思った、しかもパーツが動くドメインは変なドメインなので 「相対パスを絶対パスに変換する」この手も使えなかった。http://www.orkut.comベタ書き、駄目だなー
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Open Social Test"> <Require feature="opensocial-0.5"/> </ModulePrefs> <Content type="html"> <![CDATA[ <script type="text/javascript"> function getData() { var req = opensocial.newDataRequest(); req.add(req.newFetchPersonRequest('OWNER'), 'owner'); req.send(onLoadFriends); } function onLoadFriends(dataResponse) { var owner = dataResponse.get('owner').getData(); var url = owner.getField(opensocial.Person.Field.PROFILE_URL); url = absolutePath(url); insertScript("http://fooo.name/tako3/json/likely/"+url); } function tako3(res){ var output = ""; for(var i=0; i < res.length; i++){ output += "<li>"+res[i]+"</li>"; } document.getElementById("foooList").innerHTML += "<ul>"+output+"</ul>"; } function insertScript(url){ var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.body.appendChild(script); } function absolutePath(path){ return "http://www.orkut.com"+path; } _IG_RegisterOnloadHandler(getData); </script> <div id="foooList"> </div> ]]> </Content> </Module>
fooo.nameから検索して一発でLDR購読するブックマークレットとGreasemonkeyスクリプト Fooo rss
2007-11-04
何か見ているページに関連するページをfooo.nameから検索して、一発でlivedoor readerのフィード購読画面へ飛ばすブックマークレットを作った↓
何かページを見ているときにこのブックマークレットを実行すると、そのURLの持ち主が持っている他のページをfooo.nameから検索、見つかった場合はLDRのフィード購読画面へ飛ばす。こんな感じ↓
この場合だと http://blog.tkmr.org/ の持ち主である自分が持ってる、はてブ/del.icio.us/LDR/twitter/tumblr・・・なんかのRSSが列挙されている。見つからなかった場合はfooo.nameの情登録ページへ飛びます。もし判る情報があれば登録・追加して頂けると助かります:)
ちなみに fooo.name の説明はこちら
あと、一人当たり数十件くらい楽に表示されて煩雑になるので、そのフィードの概要を確認するためのGreasemonkeyスクリプトも書いた。
LDRのフィード購読画面で↑の画面のように、最新4件のエントリタイトルを表示する。
このGreasemonkeyスクリプトを入れておけば。
Webを眺めているときに気になる人(ページ)を見つけた
↓
その持ち主が持っている他のページをfooo.nameから検索
↓
LDR購読画面でエントリ概要が表示されるので、追いかけたいフィードを吟味して購読
という流れが割と手軽にできるかなと。
ーーー以下余談ーーーー
例えば何か面白い記事を見かけたとして、その記事をはてなブックマークに投げて終わりじゃなくてそのブログを購読して、さらにその人の他のサイトも購読して、その人のはてブやTwitterも購読して、ってのが生まれれば良いなと思う。幸い1000件くらいは楽に追いかけられるLDRっていう道具があるんだし。はてブの "人気エントリー" を追いかけるよりは、自分が興味ある人のブックマークを追いかけた方が面白いし、またその方がはてブ全体の情報に多様性が生まれて健全だと思う。というか別にはてブに限定した話じゃなくて、Web全体として。
皆が一つの人気ランキングを見てその記事をブックマークすれば、ランキング上位の物はより上位に、下位の物はより下位に行くのは当然の話で、有名な情報はより有名に、無名な情報は無名なまま、という状況が起こりそうでまずい。じゃあそれを防ぐにはどうすれば良いか、と言われると答えは判らないけど、人の繋がりがキーになるんじゃないかなと考えている。人と人の繋がりが滑らかなクラスタリングを作って、上手く分散する。また逆に分散しすぎないよう多趣味な人間がブリッジになってクラスタを繋ぐ。じゃあどうやって実現するのが良いかと言うと難しいんだよな・・・
ーーーー追記ーーーーー
tako3.comで同じことをやる場合はこちら↓
あと検索がどうも遅いなと思ったら、DBにインデックスを一つ貼り忘れていたので、修正しました。
http://fooo.name/accounts/search/http://twitter.com/otsune
修正前はやたら遅かったですが、普通になったかな。
[fooo.name] バグFIXと機能追加 fooo.name
2007-10-29
サイト情報も少しずつ溜まってきたので、サイト情報検索を作った。アカウント情報検索とほぼ同じ
サイト情報検索 http://fooo.name/sites/search/{検索したいURL}
アカウント情報検索 http://fooo.name/accounts/search/{検索したいURL}
複数件ヒットした場合は、複数結果として返る。
例えば、ffffound.comを検索してXML形式で取得するなら
GET http://fooo.name/sites/search/http://ffffound.com?format=xml
<?xml version="1.0" encoding="UTF-8"?>
<sites>
<site>
<created-on type="datetime">2007-10-14T12:19:22+00:00</created-on>
<favicon-url>http://ffffound.com/favicon.ico</favicon-url>
<id type="integer">110</id>
<name>FFFFOUND!</name>
<pattern>http://ffffound.com/home/[name]/found/</pattern>
<updated-on type="datetime">2007-10-14T12:19:25+00:00</updated-on>
<url>http://ffffound.com/</url>
</site>
</sites>
pattern = Regexp.new("http://ffffound.com/home/[name]/found/".gsub(/\[name\]/, "([^/]*)"))
acount = "http://ffffound.com/home/hogehoge/found/".scan(pattern)[0][0]
http://ffffound.com/home/hogehoge/found/ からアカウント名 hogehoge を取り出せる。
でこれを使って「OPMLのURLと探したいサービスのURLを入れれば、そのサービスを使ってる人のOPMLが出てくる」みたいな処理作れば、色々応用できないかな。例のOPMLからTwitter購読の応用として
OPML2OPMLみたいな
いつも読んでるRSSの持ち主をtako3.comとfooo.nameから探してTwitterでFollow Fooo rss
2007-10-27
ちょっと面白かった。
まずLDRで購読してるRSSをOPMLでエクスポート、次に tako3.com と fooo.name からURL全件取得。OPMLとJSONをパースしてtwitterのアカウントを探して一括Follow。
良いな〜、面白そうなので真似してみる。
まずLDRからOPMLエクスポート、全部ってのは多すぎるのでレート "5" のフィードのみに絞った。
この方法を使った http://d.hatena.ne.jp/margin/20070707/livedoor_reader_fastladder_rate
あとはRubyでパース、パース、パース
レート 5 のフィードが160個くらい、うちTwitterのアカウントが見つかったのが50人くらいだった。(むむ、もっとデータを充実する必要があるな>fooo.name)
でTwitterAPIから大量Follow :)すでにFollowしてる人はエラーになる、結果25人くらい。
こんな感じでやりました。
require "rexml/document" require "open-uri" require "net/http" opml_name = "export.xml" #Download from LDR!! tako3 = "http://tako3.com/json/all" fooo = "http://fooo.name/tako3/json/all" twitter_path = "/friendships/create" twitter_account = "USERID" #twitter account twitter_password = "PASSWORD" #twitter password #opml to twitter url--------------------------------------------------------------- twitt_maps = {} [tako3, fooo].each do |name| open(name) do |file| file.each_line do |line| urls = line.scan(/"(.*)"/)[0][0].split(" ") if twitter_url = urls.find {|url| url =~ Regexp.new("^http://twitter.com/") } urls.each do |url| twitt_maps[url] = twitter_url end end end end end opml_twitters = [] REXML::Document.new(open(opml_name)).elements["/opml/body/outline/outline[@title='5']"].each do |element| url = element.attributes["htmlUrl"] if element.class == REXML::Element opml_twitters << twitt_maps[url].chomp("/").scan(/twitter\.com\/(.*)/)[0][0] if twitt_maps[url] end opml_twitters.uniq! #do post to twitter---------------------------------------------------------------- opml_twitters.each do |name| req = Net::HTTP::Post.new("#{twitter_path}/#{name}.xml") req.basic_auth twitter_account, twitter_password Net::HTTP.start("twitter.com", 80) do |http| res = http.request(req) p res.body end end
Six ApartのRelationship Update Streamをグラフ描画してみた Fooo Web
2007-10-26
mixiエンジニアブログで知ったのですが、Six ApartがWebサービスのソーシャルデータ共有のために Relationship Update Stream という実験を始めたそうですね。
詳細は先ほどのSix Apart公式サイトやmixiエンジニアブログを見て頂くとして、ざっくり言うと「ソーシャルな情報を扱うWebサービスで、人と人の関係に変化が合った場合(フレンドになった等)Six Apartへ通知、溜まったデータは自由に共有・利用できる」という物らしい。データ取得はGETで http://updates.elsewhere.im/atom-stream.xml へアクセス、データ更新はOAuthで認証した後AtomFeedをPUTするだけとシンプル。
手始めにGraphvizでグラフ描画してみました。
以下、手順メモ
まずGETでデータ取得、GETリクエストするとデータがストリームで流れ続けるというのが面白い。でもちょっと試すには使いづらいのでファイルに落とした
wget http://updates.elsewhere.im/atom-stream.xml
試しに10分くらい落としてみた。あとは普通にファイルなので下記のようなRubyスクリプトで解析、一応ストリームを意識してREXML::Parsers::StreamParserで解析してみた(HTTPでストリーム的にデータを読み込む、とかRubyでどうやるの?TCPSocket使って実装するのかな)
require 'rexml/parsers/streamparser' require 'rexml/parsers/baseparser' require 'rexml/streamlistener' class RUSListener include REXML::StreamListener attr_accessor :relation, :name_map def initialize @relation = {} @name_map = {} @current = [] end def tag_start(name, attributes) if name=="link" && attributes["rel"]=="related" @current << attributes["href"] @name_map[attributes["href"]] = attributes["title"] unless @name_map[attributes["href"]] end end def tag_end(name) if name=="entry" unless @relation[@current[1]] @relation[@current[1]] = [@current[0]] else @relation[@current[1]] << @current[0] end @current = [] end end end File.open("./rel_test.dot","w") do |file| source = File.read "./atom-stream.xml" listener = RUSListener.new REXML::Parsers::StreamParser.new(source, listener).parse outputter = [] listener.relation.each do |key, value| value.each do |from| if listener.relation[from] && listener.relation[from].size > 1 outputter << "\"#{listener.name_map[from]}\" -> \"#{listener.name_map[key]}\";\n" end end end file.write("digraph Relation {\n #{outputter.join} }\n") end
参考にした > http://www.nslabs.jp/ruby-rexml.rhtml
あと、データが短期的すぎてネットワークに集約しないので、ある程度フィルタリングした。
Graphvizで出力
dot -Tpng rel_test.dot -o rel_test.png
出力結果がこれ(見やすく切り取っています)
むむ、やっぱある程度データの量貯めないと面白くないな。一週間くらい溜め込んで試してみたい。
ー以下余談ー
というか fooo.name でこんな情報の収集を早くすれば良いって話か。でもSNSなんかのフレンド情報を fooo.name でストアするのは問題あるよな、とも最近思ってきた
・「リンク情報を保存してる」と言い張る
・文句がついたら、そのサイト情報は保存しないようブロックして対応する
で逃げられないかな?
twitter繋がり = //a[@rel="contact"]
del.icio.us繋がり = //li[@class="netuni"]/a[@class="uname"] ( http://del.icio.us/network/xxx のページで)
みたいにXPathでフレンドURLをサイト毎に定義してスクレイピングする。ってのが良いかな、と思うんだけどな。
いちいち有象無象のフレンド情報取得APIを叩くのはめんどくさい、サービス毎に実装すんのかよみたいな
fooo.name更新 - バグ潰しとtako3.com互換API Fooo
2007-10-15
fooo.name 幾つかバグ潰しと、API更新 しました。
主な変更点はtako3.com互換APIの追加です。詳細な情報はこちらをどうぞご確認ください。
次はGreasemonkeyスクリプトでAPIの実験みたいなのをやろうかな。あと、今後のTODOをまとめ
TODO:
・同一アカウント&別人問題の対応
さすがに今のままだと、誤情報が混じってくるのである程度フィルタリングを設けたい。
A) 人と人の繋がりで判断。複数サービスで "tkmr" が同じ "hoge" さんをフレンドにしていれば、その複数サービスの "tkmr" は同一人物とみなす(一致率がある閾値を超えたら)
B) 各ページの特徴語をベイズフィルタに掛ける。自分だったら "tkmr", "ruby", "rails" なんかの頻出語、URL、フレンドのアカウント名等を複数ページで調べる。少なくともこれで海外サービス等の別人はフィルタリング(グルーピング)できるかも。(たとえば "tkmr" の場合は http://www.myspace.com/tkmr )
あとベイズフィルタで閾値を設ければ、死にアカのフィルタもできるかも。作ったけど全く更新していないページ等はマッチしないので。というかB方式はA方式を内包するのか。
・フィルタリング選択
用途によって求められる精度も違うと思う。ゆるい用途ならフィルタも要らないし、ある程度ならベイズフィルタ、さらに信頼できる情報が必要なら「同一セッションで複数サービスの認証を通った証明」フィルタ、とか選択可能にしたい。http://fooo.name/accounts/tkmr?filter=high みたいな
・繋がり情報の収集
サイトのURLパターンはあるので、各ページにある "フレンドへのリンク" のURLをXPath等で設定可能にすれば収集できるかも。
twitter繋がり = //a[@rel="contact"]
みたいな。で http://twitter.com/[name] にマッチすればフレンドのアカウント名も取れるし。人と人の繋がりがHTMLでも提供されてれば汎用的に行けるかな。
・URLの登録拒否サポート(ブロックリスト)
今は細々とやってるので良いかなと思うけど、今後を考えると「私のURLを登録しないで!」というサポートフォームみたいなのは付けた方が良いかな。ブロックリストみたいな。
でもどうやって本人を確認するか、認証APIがある & OpenIDなら認証を通ればOKとできるが。。
ソーシャルグラフ共有サイトと言うかなんと言うか、Fooo.name!!というWebサービスを作った Fooo Web
2007-10-11
(最後、少し追記しました)
最近、本業も暇になってきた & 連休続きだったので、ひとつWebサービスを作ってみました。
で、どんな物なのか
と言うと、Web上で誰か気になる人を見つけたときに「その人が、他にどんなページを持ってるか?」という情報を探すことができます(運が良ければ)
具体的には、このブックマークレット 「Fooo.nameから探す」を使ってもらうのが判り易いと思います 何か適当なWebサイト上でこのブックマークレットを実行すると、そのサイトに関連するURLを探し表示します。
見つかった場合 : http://twitter.com/tkmrを検索
無事関連するURLが見つかった場合は、他のページを見て回るもよし。LivedoorReaderで「まとめて購読」するリンクを設けているので、男らしく全部まとめて購読するのも良いと思います。
残念、見つからなかった場合:http://twitter.com/hogeを検索
見つからなかった場合は、ぜひ本サイトへ情報を登録してやってください;)ついでにその人が使っているアカウント名等を合わせて登録して貰えると大感激です。 「大抵の人は複数のWebサービスで同じアカウント名を使う」「大抵のWebサービスはユーザ固有ページURLにアカウント名を使う」というゆる0い方針のもと。アカウント名が追加されると、自動で情報収集を試みます。
もちろんhogeさんの個人ブログは自動で登録されない or 別人のhogeさんが持っているページが登録される、等の問題もありますが、現状サイト内の情報はほぼすべて自由に編集できるので、wiki的に編集して補正して貰えると大感激です。
認証について
ですが一応OpenIDによるログイン認証を設けています。本来は「livedoor関連サービスはlivedoor OpenIDでログインした場合のみ編集可能」等とするのが正しいと思います、ですが現状あまりOpenIDが普及していない & できるだけ編集のハードルを上げたくない。ということで未ログインでもほぼ編集可能です。例外として http://twitter.com/[name] というサイト情報パターンの編集だけは認証を掛けています、少し影響が大きいので。
とりあえず現状 HTML / XML / JSONP / RSS & Atom の形式で情報を取得できます。下記のように好きなページへ拡張子を付けることで、その拡張子に従った形式のデータを返します。
XML - http://fooo.name/accouts.xml
JSONP - http://fooo.name/accouts.json?callback=hogeCallback
RSS - http://fooo.name/accouts.rss
Atom - http://fooo.name/accouts.atom
例:アカウント "tkmr" の情報 HTML - http://fooo.name/accounts/tkmr
XML - http://fooo.name/accounts/tkmr.xml
例:http://twitter.com/tkmr で検索 HTML - http://fooo.name/accounts/search/http://twitter.com/tkmr
JSONP - http://fooo.name/accounts/search/http://twitter.com/tkmr? format=json&callback=hogeCallback
JavaScriptならJSONPでお手軽に。RailsならActiveResourceでフルアクセス。詳細は下記のAPIページをどうぞご覧ください。 http://fooo.name/main/aboutapi
何が目的? & 何故作ったか
についてですが、単純に言うと「人と人の関係がAPIで取得できたら面白いんじゃないか」という思いがあります。「mixiの友達が3人以上購読したRSSは自動で購読」「Twitterのフレンドが一定数以上引用したURLを開く。一定数以上含まれたHotなキーワードをBlog検索して結果のRSSを購読・・・・」などなど、それがWebサービスを横断して色々できると面白いかなと常々思っていました。これは最近話題のソーシャルグラフというキーワードが上手く表現していると思います。(下記のページが判り易いと思います)
また少し話は逸れますが、下記のページにある SNS型RSSリーダー というアイデアを見つけたのもきっかけの一つです
なめらかなメッセージング- ised議事録 - 2.設計研第7回:鈴木健
上の記事によると、インターネットの登場には「マスメディアによるメディアの独占を崩壊させ、多様な個人が世界中の人々とコミュニケーションする」という夢があったが、現状達成されていないし、良い方向に向かっているとも思えない。なぜなら人間の認知力には限界があるので情報を上手くフィルタリングしないと下記の問題が発生する
1) マスメディア化 結局、普通の人が見るのは有名なページ or サイト。そのためインターネットが既存のマスメディアを破壊しても、第2, 3のマスメディア(Yahoo , Google , 一握りの有名サイトと無数の無名サイト)が発生する
2) たこつぼ化 自分の興味のある閉じられたコミュニティの情報にしか耳を傾けなくなる。(本屋に行くとき「本を買う」というのはもちろん、何か新しい分野の情報に出会う、ために本屋をぶらつくということがあるかと思います。逆に検索エンジン中心のWebは興味の無い情報に出会う確率が低い、そこで上手い配分で偶然性を入れる はてブやTumblrが流行っている?)
3) SPAM化 無理矢理外部から「たこつぼ」の中に情報をプッシュすると、本来有益な情報もSPAMになる。(livedoor Reader なんかはもっと多くの人に使ってほしい、と個人的に思っているけど、それを現状必要としていない人に伝えるのは至難の技だと思う。必要としてない情報は(実は有益でも)邪魔なSPAMになってしまう)
これらの問題を解消するために、人と人の繋がりを利用した「SNS型RSSリーダー」はどうか?というアイデアが面白いと思う。
ざっくり言うと「友達の読んでいるRSSが流れてくるRSSリーダ」のようです。詳細は上記ページを見て頂くとして、既に繋がりのある友達からの情報なら興味を持ち易いし「高校の友達フィード」から全然違う分野の情報が流れ込んでくれば、別分野に興味を持つ確率も高まる(SPAM化、たこつぼ化の解消)マスメディア化に関しては決定的な解消と行かないかもしれませんが、中心の無いネットワークが結びつくので比較的マスメディア化も分散するのではないか、という考察がされています。
アイデア自体は面白いと思うのですが、ここは 「ソーシャル情報によるRSSアグリゲート / UIとしてのRSSリーダ」と実装を分離することができると思います、そしてUIに関しては既存のRSSリーダを利用し、ソーリャル情報によるRSSアグリゲート機能も「ソーシャル情報のDB / RSSアグリゲータ」と分離するべきで、とりあえず「ソーシャル情報のDB」を実装してみよう、と思い試みた結果できたのが本サイトになります。
次の段階
ということで次の段階としては、人と人の繋がりの情報を収集する仕組みを実装しようと思います。 例えば「[翻訳] ソーシャルグラフについて」のページで言う "エッジ" の実装になるのかな、と考えています。
追記
少し追記を。今回作成したFooo.nameと同じコンセプトのWebサービスに http://tako3.com/ というサービスがあります。
同じコンセプトのサービスがあるなら、車輪の再発明をする必要ないじゃん。とも思いましたが、tako3.comでは「量より質」の方針で集めているようで 、個人的に「質より量」で実装してみたかったのでやってみた次第です。
(というか、昨日作者の方のブログへコメント出しておきながら、言及を忘れてました。。申し訳ないです)
ということで tako3.com も合わせてどうぞ :-)





