Category Archives: data

data

Wedataのiview用データベースSITEINFO仕様

ベータバージョンiviewAutoPagerizeみたいにsiteinfoを書けばどんなサイトでもiviewでから見られるようになりました。

Img 0005

そのデータベース: iview – wedataの今のところの仕様です。

はじめに

iviewはAutoPagerizeLDRizeがやっているのと同じように、HTMLページを読み込んで、その中にある写真や説明文をXPathで取り出して表示しています。そのページの写真を全部表示し終えると、AutoPagerizeのようにXPathで表された次のページへのリンクをたどり、そのページの中にある写真を表示していきます。

サイト構造の分類と定義

データベースの各項目の意味を説明する前に、一般的な写真サイトのHTML構造について3種類分類します。多くのサイトは、以下で述べる3種類の構造のどれかに当てはまります。

たいていの写真サイトは、写真に限らず大量のコンテンツを管理しているサイトがそうしているように、ひとつのページにすべての写真を表示したりはせず、それらをたくさんのページに分割して表示しています。たいていは、ひとつひとつのページはリンクで接続されていて、リンクをたどっていくことですべての写真を見ることができます。このひとつのHTTPリクエストで取得できるHTMLを”ページ”と呼びます。

ひとつのページの中には、通常10程度のコンテンツが含まれています。コンテンツは多くの場合、文章と写真の組み合わせです。ブログであれば各エントリがコンテンツにあたります。ここではLDRizeに倣ってこのコンテンツを”paragraph”と呼びます。

Flat

ひとつのparagraphには、ひとつ以上の写真が含まれていることがあります。FFFFOUND!Flickrはひとつのparagraphに写真がひとつずつ含まれています。この構造になっているものを便宜的にflatと呼びます。

Flat

Split&Flatten

ひとつのparagraphに複数の写真が含まれているものにはAsian Photography BlogFace Hunterがあります。ふつうのブログを主に写真を紹介するために使っているものには、この構造になっているものが多く見られます。この構造になっているものはiview内で各paragraphの中で写真を取り出してそれを並べていることからsplit&flattenと呼びます。

Split&Flatten

ReadMore&Split&Flatten

paragraphには写真が含まれておらず、paragraphに入っているリンクをたどった先に写真が含まれているものもあります。リンクをたどった先のページ同士にはリンクがないので次のページに進むには元のページのリンクを使う必要があるものです。Behance Network :: Gallerylastnightspartyがこの構造になっています。この構造はreadMore&split&flattenと呼びます。

ReadMore&Split&Flatten

多くのサイトはこの3つの構造のどれかに当てはまります。
iviewのSITEINFOは、この3つの構造を表現するためのものになっています。

データベースの各項目の意味

iviewのSITEINFOには以下の項目があります。先頭に*がついているものは必須の項目です。subParagraphから始まるものはsplit&flattenのサイトを定義する時に使います。subRequestから始まるものはReadMore&Split&Flattenのサイトを定義する時に使います。
flatのサイトは、先頭に何もついていないurl, paragraph, nextLink, caption, permalink, imageSourceで定義することができます。

*url
*paragraph
nextLink

caption
permalink
imageSource
imageSourceForReblog

subParagraph.paragraph 	
subParagraph.cdata
subParagraph.caption
subParagraph.permalink 	
subParagraph.imageSource 	
subParagraph.imageSourceForReblog

subRequest.paragraph 	
subRequest.caption 	
subRequest.permalink
subRequest.imageSource 	
subRequest.imageSourceForReblog 	

url

urlは最初に読み込むページのURLです。iviewはurlに指定されたページをロードしてきます。ページはHTMLまたはXMLで記述されている必要があります。

nextLinkは次にロードするべきページのURLを表すXPathです。XPathがa要素またはlinkにマッチする場合、iviewはそのhref属性をURLとして利用します。逆に言えばalinkのときには@hrefを省略することができます。

paragraph

paragraphはページの中にある繰り返しの要素ひとつひとつを表すXPathです。ほかの項目(caption, permalink, imageSource, imageSourceForReblog)はこのparagraphのXPathにマッチした要素それぞれからの相対的なXPathとして表現します(LDRizeと同じかんじです)。

permalinkは写真の存在しているページのURLを表すXPathです。このURLにアクセスすれば、そのページのどこかに目的の写真が存在しているべきURLを表します。subRequest.paragraphが存在するときには、読み込むべきページのURLを示します。

このXPathはparagraphにマッチした要素をコンテキストとして表現されています。
XPathがa要素にマッチする場合、iviewはそのhref属性をpermalinkのURLとして利用します。
SITEINFOにsubParagraph.paragraph, subRequest.paragraphの両方が存在しない時(サイトの構造がflatのとき)に参照され、その場合必須の項目になります。

caption

captionは写真の説明となるテキストを表すXPathです。

このXPathはparagraphにマッチした要素をコンテキストとして表現されています。
SITEINFOにsubParagraph.paragraph, subRequest.paragraphの両方が存在しない時(サイトの構造がflatのとき)に参照されます。

imageSource

imageSourceは写真のURLを表すXPathです。imageSourceで表されたURLは画面に写真を表示する時に用いられます。

このXPathはparagraphにマッチした要素をコンテキストとして表現されています。
XPathがimg要素にマッチする場合、iviewはそのsrc属性をimageSourceのURLとして利用します。
SITEINFOにsubParagraph.paragraph, subRequest.paragraphの両方が存在しない時(サイトの構造がflatのとき)に参照され、その場合必須の項目になります。

imageSourceForReblog

imageSourceForReblogは写真をshareするときに使うURLを表すXPathです。iview for iPhoneのように画面の小さいデバイスでは、ピクセル数の少ないサムネイルでも十分な表示結果が得られる場合があります。しかしshareするときには可能な限りピクセル数の多い写真をshareするべきです。デバイスに表示するよりも高い品質の写真を利用できる場合にはそのURLをimageSourceForReblogで表現することで、デバイスに表示する写真のURLとは別にshareする写真のURLを指定することができます。

このXPathはparagraphにマッチした要素をコンテキストとして表現されています。
XPathがimg要素にマッチする場合、iviewはそのsrc属性をimageSourceForReblogのURLとして利用します。
SITEINFOにsubParagraph.paragraph, subRequest.paragraphの両方が存在しない時(サイトの構造がflatのとき)に参照され、その場合必須の項目になります。

subParagraph.paragraph

subParagraph.paragraphはサイトの構造がSplit&Flattenのときに、ひとつのparagraphの中に含まれている繰り返しの要素ひとつひとつを表すXPathです。ほかのsubParagraph.で始まる項目はすべてsubParagraph.paragraphにマッチした要素をコンテキストとして表現します。

subParagraph.cdata

subParagraph.cdataはAtomやRSSのようなXMLに含まれているCDATAの中身をHTMLとしてパースしたいときに、そのパースしたいCDATAの親要素をXPathで表します。

SITEINFOにsubParagraph.cdataがある場合、subParagraph.で始まる項目はsubParagraph.paragraphにマッチした要素ではなく、subParagraph.cdataにマッチした要素の小要素のCDATAをHTMLとしてパースした時のルートノードがコンテキストになります。

subParagraph.*

subParagraph.で始まるその他の項目は、XPathのコンテキストがsubParagraph.paragpraphにマッチした要素になること以外、subParagraph.のつかない項目と同じ意味を持っています。

subRequest.paragraph

subRequest.paragraphはサイトの構造がReadMore&Split&Flattenのときに、
permalinkで指定されたURLのページのドキュメントの中の繰り返しの要素ひとつひとつを表すXPathです。

ほかのsubRequest.で始まる項目はすべてsubRequest.paragraphにマッチした要素をコンテキストとして表現します。

subRequest.*

subRequest.で始まるその他の項目は、XPathのコンテキストがsubRequest.paragpraphにマッチした要素になること以外、subRequest.のつかない項目と同じ意味を持っています。

ようするに

表現したいサイトの構造ごとになにを定義すればいいのかを下に示します。*をつけたものは必ず定義する必要があるものです。

Flat

*url
nextLink
*paragraph
  *permalink
  caption
  *imageSource
  imageSourceForReblog

Split&Flatten

*url
nextLink
*paragraph
  *subParagraph.paragraph
    *subParagraph.permalink
    subParagraph.caption
    *subParagraph.imageSource
    subParagraph.imageSourceForReblog

subParagraph.permalink, subParagraph.captionのふたつは、各subParagraph.paragraphで変化しないことが多いため、かわりにpermalink, captionで指定することも可能です。その場合

*url
nextLink
*paragraph
  *permalink
  caption
  *subParagraph.paragraph
    *subParagraph.imageSource
    subParagraph.imageSourceForReblog

を定義すればokです。

ReadMore&Split&Flaten

*url
nextLink
*paragraph
  permalink
  *subRequest.paragraph
    *subRequest.caption
    subRequest.permalink
    *subRequest.imageSource
    subRequest.imageSourceForReblog

subRequest.permalinkは省略可能です。省略した場合permalinkのURLが利用されます(注: iview for Tomblooで正しく機能しないかもしれません)。

謝辞

なにもドキュメントのない状態で一畳プラレールなおとくんの自由研究のSITEINFOを書いてくださったdotimpactさんありがとうございます。

途中まで書いて放置していたこのpostを書く力となりました。

Tomblooハックス – reblookmark, retweet とクリップボード

ひとのブックマークを見ていて、自分もそれブックマークしておきたい、というのがあります。

と思っても、一度そのリンク先を開いて、ブックマークレットを呼び出して、自分でタグを書いて、ボタンを押さないといけません。ちょーめんどう。rebookmarkできたら自分でタグとか入れなくて良くてちょーべんり。

Tomblooにはてなブックマークとdel.icio.us用のextracterを作りました。

コード

tombloo/chrome/content/libraryにパッチ、ファイルをコピーします。

rebookmark

はてなブックマーク – COLLECTION & COPYとかをひらいて、リストされているブックマークのどこかをクリックします。そうするとブックマークをLinkのコンテンツとして認識してくれます。

ここから先はふつうのTomblooで何かポストする時と同じ。メニューのShare.../Link HatenaBookmarkを選ぶとウインドウが出てきます。

もともとついていたdescriptionとタグに加えて、via:がつきます。ブラジル方式。
どこにpostしてもいいですがはてなブックマークにpostするとこうなります。

もともとついていたタグもコメントもそのまま。

del.icio.usからも同じようにメニューのShare.../Link del.icio.usからrebookmarkできます。
ちょっとXPathを書けばほかからもrebookmark可能。

もともとつけられているタグ、メモが取り出されるので、あとはふつうと同じようにpostしたいサービスを選んでpostする。del.icio.usにpostしてもいいしはてなブックマークでもいいしtumblrでもいい。

retweet

もうひとつやりたかったのがtwitterで誰かの発言をそのまま発言すること。誰かが”ええーーー”って言った時に自分も”ええーーー”っていいたい気分だったら”ええーーー”って言いたいみたいなかんじ。”えええーーー”だったら自分で入れるの簡単だけど、簡単じゃないやつだと、入れてる間に自分もそれまねして言いたいかんじがなくなっちゃう。

rebookmarkとおんなじかんじで、発言の上で右クリックしたらShare.../Quote Twitter/Timelineっていうのが出てくる。メニューの項目名はてきとう。

あとはいつもとおなじ。

投稿先をtwitterにするとこうなる。

Twitter / 33とtinyurl部分はTomblooのTwitter posterが、受け取ったデータのタイプがlinkだった時に受け取ったメタデータをtwitterにpostできる形式に変換する過程で付加されている。

retweetされたものをretweetしたりすると140文字制限をすぐに超えて破綻する。tumblrでもたくさんreblogされているやつはblockquoteでひどいことになる。

クリップボード

コードを書いているうちに、Windowsのクリップボードを思い出した。

エクセルのセルをクリップボードにコピーして、ノートパッドに貼付けるとセルに入っていた文字列が貼付けられる。これがどうやって実現されているか。”エクセルのセル”という型だったデータがどうやって”ただのテキスト”に変わるのか。

Win32::Clipboard – Interaction with the Windows clipboard – search.cpan.orgのインターフェイスを見ればだいたいの仕組みがわかる。アプリケーションはユーザからコピー要求がきた時に、アプリケーションが現在選択しているデータをクリップボードに登録する。このときデータのフォーマットを一緒に登録する。WindowsはデフォルトでCONSTANTSに書かれているような形式をサポートしている。テキストデータ、ビットマップ、waveなどなど。エクセルのように、独自の形式のデータのときにはあらかじめエクセルのためのフォーマットを登録しておくと、エクセル内部のデータ形式をクリップボードに登録できる。

一方クリップボードからデータをペーストする時、ノートパッドはまずクリップボードにあるデータが、ノートバッドで利用できる形式(テキストデータ)として取得できるかをIsText()で問い合わせる。ここから先は推測。クリップボード(Windows)はノートパッドから問い合わせを受けると、自分が今持っているデータが問い合わされた形式(テキストデータ)に変換できるかを判断する。エクセルによって登録されたフォーマットのデータがテキストデータに変換できるかどうかはエクセルに聞かないとわからないのでエクセル(実際にはエクセルのプロセスじゃなくて登録されたフォーマットに結びつけられたDLLの中の何かだと思う)に聞きにいく。エクセル(のDLL)は問い合わせを受けた型に変換できるかをクリップボード(Windows)に返す。クリップボードはエクセルから変換可能だと言われたので、それをそのままノートパッドに伝える。ノートパッドは変換可能だと言われたので、クリップボードからテキスト形式でデータを取得しようとする。そうするとまたクリップボードはエクセル(のDLL)に今クリップボードにあるデータをテキストデータとして変換するためのリクエストを出す。エクセルはクリップボードのデータをテキストデータに変換して返す。そして最終的にノートパッドに、エクセルのセルデータがテキストデータとして貼付けられる。

ノートパッドで扱うことのできないエクセルのデータが、クリップボードを介してノートパッドで扱えるテキストデータに変換されているように見える(実際にはエクセルがテキストデータにしてほしいと要求されて、エクセルがテキストデータに変換しているはずだ)。

(@IT:Windows TIPS — Tips:クリップボードの中身をのぞき見る方法にはすべての形式でクリップボードにあらかじめ用意されているような書き方がされているが、変換可能な形式は多数あるはずなので、それぞれの形式でデータをあらかじめ用意していたらひどくメモリを浪費するはずなのでそんな実装にはなってないと思う)

TomblooはWindowsのクリップボードと同じ役割を果たしている。

tomblooの中にはextracterとposterというのがあるのだけど、extracterというのはベタなHTMLからメタデータを取り出す。その中には取り出す部分のXPathや取り出したデータをどう加工するかがjavascriptで書かれている。クリップボードとの対比でいくと、このextracterがエクセルにあたる。

例として上で紹介したretweetするときに行われている、twitterから発言のデータをquoteとして取り出してtwitterにpostする場合を考える。
twitterのextracterは、右クリックされた位置を基準にしてブックマークのデータを取り出し、そのデータをTomblooがもともとサポートしているデータ形式のひとつのquoteという型として登録する。ユーザはそのあとそのデータをtwitterにpostするように指定する。

エクセルのセルをコピーしてエクセルに張る時は、ちゃんとエクセル独自のデータ形式である”セル”として貼付けることができるけれど、twitterにpostできるデータはテキストデータだけだ。twitter extracterは発言をquoteとして取り出すけれど、twitterはその取り出されたquote形式のデータを直接受け入れることができない(というかいまのところquote形式を扱えるのはtumblrとそのクローンだけだ)。

でもquote形式のデータをtwitterで扱うことのできるただのテキストデータに変換することは可能だ。Tomblooではその変換を投稿先に指定されたサービスのposterがやっている(Windowsのクリップボードではデータソースであるエクセルがやっていた)。この場合twitterのposterがquote形式のデータをtwitterにpostできるただのテキストデータに変換している。内部的には

{
    item: "Twitter / 33",
    itemUrl: "http://twitter.com/33/statuses/852481203",
    body: "よかった~病気のucnvが戻ってきて、心配したよ"
}

という構造をしているquote形式のデータがtwitter posterによって

"Twitter / 33 http://twitter.com/33/statuses/852481203 よかった~病気のucnvが戻ってきて、心配したよ"

という文字列に変換されてtwitterにpostされている。

post先のサービスがTomblooの中でのデータ形式(Tumblrの持っている7つのデータ形式と同じになっている)と違っていた時には、このようなデータの型変換が行われてpost先のサービスに投稿される。

Quote型をサポートしているサービスとして引用βがある。
Link型をサポートしているサービスは多数ある。ほとんどのソーシャルブックマークはtomblooのlink型データを、セマンティクスを失うことなく保存することができるだろう(すくなくともdel.icio.usとはてなブックマークは可能だ)。

Tombloo上のデータはどんなデータでもテキストデータに変換することができるので(photoやaudioもURLに変換できるから)、はてなダイアリーのようにテキストデータを扱えるサービスは、もともとあったデータ構造は失われてしまうけれけど、どの形式のデータでも保存できる。

ただ、そのもともとのデータ形式が持っているデータを、どのようにただのテキストデータに変換するかは難しい。

@ku 標準的な機能と、拡張の機能の区別が難しいですよね、たとえばはてなにポストするときにclassはどうするのが標準か、reblog情報を取り去っていいのか、全部設定できるようにするか、ツール作者が一定の決めつけをするか、また飲みたいすねー
Twitter / 33: @ku 標準的な機能と、拡張の機能の区別が難しいですよ…

クリップボードの仕組みが参考になると思ったけど、クリップボードはデータソースがフォーマットの変換をやっている。tomblooはposterでやっていて、そしてむしろそのほうがいい。これはクリップボードが、”エクセルのセル”のようなアプリケーション独自のデータ形式を登録できる仕様なのに対して、Tomblooは7つのコンテンツしか存在しないからposterの側で変換が可能なんだと思う。たかだか7つしかないのはわかっているのでextracter側で変換を実装することができる。そしてpostするサービスの特性(140文字しか入らないとか、HTMLは反映されないとか)がWindowsのアプリケーションに比べ例外的なものがあるのでデータソースで一律に変換すると不都合が多い。

reblogはtumblrがtumblr内でデータの形式を保ったままでのextract -> postをpostidを指定するだけで中でやってくれるという機能のことだ。rebookmarkは、ソーシャルブックマークで共通に使われているlink形式のデータをどこかから取り出して、どこかにpostすることだ。

ITmedia エンタープライズ:MSオジーCTO、クリップボードをWeb世界に広げるも同じようなもんだけどhCalとか誰もマークアップしてないしXPathで書いたりするのも困難だ(だってみんな毎回適当に記号入れて表現してるもん)。IE8のWebSliceはどーなってるのかな。

tumblrの7つのコンテンツ

tumblrの持っている隠れた新しいところはブログのコンテンツを7つに大別したところだ。

tumblrはpostの種類を7つにわけた。ふつうの文章、写真、引用、リンク、チャット、音楽、映像。

tumblr content-types

もちろんtumblr以前にもこれらのコンテンツをブログに投稿することはできた。でもそれはふつうの文章の中に何のマークアップもされずに入っていたので、キカイにはそのpostの内容がなんなのかはわからなかった。たいていのブロギングプラットホームにはカテゴリという概念があって、その投稿が何についての投稿なのかを示すことはできたが、これはニンゲンが読むためのものだった。キカイには投稿の内容を知る術がなかった。

tumblrはProjectionist: A tumblelogに感銘を受けて作られたらしい。そしてブログに投稿できる形式を7つに制限した。おそらく、投稿するときの利便性と、投稿された内容に合わせて最適なみためを作る上で必要だったから、この7つに分類したのだと思う。ユーザはひとつのpostにひとつの写真、ひとつのpostにひとつのリンク、というような制約をうけることになった。いままでのブロギングプラットホームにはなかった制約。

コンテンツをわけることがセマンティクスをつくる

キカイの側からこのことをみると、ニンゲンが投稿の内容を判断して、内容に適したコンテンツのタイプを選んで投稿してくれているように見える。いままでのブロギングプラットホームに投稿されたpostは、キカイにとっては中に何が書かれているのかわからなかったけれど、tumblrにpostされたコンテンツは、その投稿の中身がなんなのかをニンゲンがあらかじめ判断して、記述してくれているのと同じだ。結果、tumblr上のコンテンツはキカイが適切に扱うことができる。

たとえば、MovableType上に投稿されたプログのエントリから、自動的に写真だけをFlickrにアップロードしたいとする。このときは、ユーザが写真をメインにした投稿をするときはカテゴリをphotoにする、というようなMovableTypeのシステムとして規定されていないルールを作っている必要がある。そういうルールなしに、単純にpostの中のimgをFlickrにアップロードすると、Deferredの動作を図解した画像もFlickrに上がってしまうだろう。ふつうのブロギングプラットホームは、ニンゲンが何の制約もなくコンテンツを投稿できる自由があるけれど、ニンゲンの自由な振る舞いはキカイに理解できないのでキカイが自動的に何かをするのには向いていない。MIMEに例えるなら、伝統的なブロギングプラットホームはmultipart/mixedのデータをそのまま貼っていて、tumblrはそれをひとつひとつばらばらにして、image/jpegだとかaudio/mpegとかをつけたかんじ。multipart/mixedのデータがFlickrにコピーできるかはニンゲンにしかわからないけれど、image/jpegだったらコピーできそうだ。そんなかんじ。

tumblr以前でも、フィードアグリゲータはフィードの種類を分けて扱っていた。コンテンツの種類を分けていた、というよりは、フィードを分類していた。フィードアグリゲータの流れを汲むFriendFeedはフィードの種類を8つに分けている。

FriendFeedはフィードの内容がログとして残っていくけれど、そこにはコンテンツの分類、という概念は希薄だ。あくまでfriendsのactivityをみる、というかんじで、コンテンツそのものをみるかんじは薄い。

あとがき

まだ何がいいたいのか固まってないなー。
コンテンツが7つに大別されたことが、いままで何ともいえないものだったコンテンツの型というのが、明確になり、互換性があるかないかがわかって、流通しやすくなった。pipesで画像を取り出して、MovableTypeに流し込んでも、それをreblogするときに、はたしてtextでreblogすればいいのかphotoでreblogすればいいのかは、わからない。
もちろんそれはtumblrの中だけの方言語彙の上で成立しているだけのセマンティクスなんだけど、ぞもそれがあればこれだけコンテンツを再利用したり、高速に流したりできる、というのがわかったこととか、tumblrというツールでの発見ではないか。

型違うと困るよねー。実行時型変換で変換できないとかで例外出たりするじゃん。意味のない値になったりとか。(int)”May 2008″でゼロになっちゃうみたいな。そういう型のミスマッチが事前にわかるところとかがコンテンツを扱いやすくしてる。

伝統的ブロギングプラットホームを作った連中がアホだったとかそんなわけじゃなくて、単に時間的な積み重ねから、データ型として何があったらいいのかが見えてきたということ。

あとレイヤは違うけど 最速インターフェース研究会 :: XMLはメタデータというより生データとしての利用価値が高まりつつあり、AjaxによるUIの切り離しがそれを加速する も同じことだと思う。

Google SpreadsheetsにGreasemonkeyスクリプトとかの出力を保存する

もともとは普段みてるページにどれくらいメタデータ入ってるのか知りたいからログとりたくて、ログとるのはいいけどバックエンド用意するのめんどい、というのから。Google Spreadsheetsがわりと楽そうでした。

デスクトップアプリケーションですら、クラッシュレポータなんてものがついていて、ネットワーク経由でアプリケーションの異常を送信できるのだから、Greasemonkeyスクリプトでもおんなじことする、とかにも使えます。

記録用のシートを作る

シートにカラムは作っておく必要があります。シートに書かれてないパラメータは送ってもシートに記録されません。

キーとワークシートID

Google Spreadsheetsのドキュメントは、Excelでいうファイルがキーというもので管理されてて、ExcelでいうシートがworksheetIdというので管理されてます。これを知るのにAPI経由だとめんどくさいので、書き込みたいシートonlyでpublishして

more publishing optionsでRSSのURLをみるのが一番はやいです。

feeds/listの後にある長いやつがキーで、数文字のやつ(上のスクリーンショットだとod6)がworksheetIdです。

スクリプト

自分のシートに自分で記録する場合。自分のシートに人に書いてもらう場合は明示的な認証とか必要になってきてめんどくさくなります。

var key = 'o09670103056371318321.5625818402836282329';
var sheetid = 'od6';
function GoogleSpreadsheets(key, sheetid) {
  this.key = key;
  this.sheetid = sheetid;
  
}
GoogleSpreadsheets.prototype.toSpreadSheetXML = function (data) {
    var xml = ['<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://schemas.google.com/spreadsheets/2006/extended">'];
    for (var name in data) {
      xml.push( 
        '<' + name + '>' +
          data[name].replace(/</g, '<').replace(/</g, '>') + '</' + name + '>'
      );
    }
    xml.push('</atom:entry>');
    return xml.join("\n");
  }
GoogleSpreadsheets.prototype.addRow = function (data, onload) {
  var opts = {
    url: "http://spreadsheets.google.com/feeds/list/" + this.key + "/" + this.sheetid + "/private/full",
    method: "POST",
    headers: {
      "Content-Type": "application/atom+xml"
    },
    data: this.toSpreadSheetXML(data),
    onload: onload
  };
  GM_xmlhttpRequest(opts);
}
var sheet = new GoogleSpreadsheets(key, sheetid);
sheet.addRow( {
  url: document.location.href,
  generators: generators,
  relnext: relnext,
  relalternate: relalternate,
  relmeta: relmeta,
}, function (res) {
  console.log(res);
});

これを実行するとシートに1行ずつデータが追加されていきます。
Spreadsheetsの裏側にcomet入れてあるのかなんなのか、シートを開いているとデータが追加された瞬間にシートの表示にも反映される。

Google Spreadsheetsのドキュメントによると、10,000レコードまでしか記録できないのでそこは注意。

なんかあとFill formとかいうモードがあるらしいので、もしかしたらそれ使うと安全に(悪い人にカラム名を書き換えられたりすることなく)不特定多数に使ってもらえるのかも。

セルベースの場合

Spreadsheets APIにはデータの変更モードにrow-basedとcell-basedっていうのがあってcell-basedだとすごくめんどくさい。cell-basedのときはセルのバージョン(セルじゃなくてシートかも)をとってきて、それをパラメータにつけて送らないとだめ。

細かいこと忘れたけどZend_GDataが送信してたデータのログ貼っとく。一番難しいのはxmlどう書いたらいいのかわかんないことだった。

rawdata: <atom :entry xmlns:atom="http://www.w3.org/2005/Atom"><atom :category term="http://schemas.google.com/spreadsheets/2006#cell" scheme="http://schemas.google.com/spreadsheets/2006"/></atom><atom :id>http://spreadsheets.google.com/feeds/cells/o09670103056371318321.5625818402836282329/od6/private/full/R1C1</atom><atom :link href="http://spreadsheets.google.com/feeds/cells/o09670103056371318321.5625818402836282329/od6/private/full/R1C1" rel="self" type="application/atom+xml"/><atom :link href="http://spreadsheets.google.com/feeds/cells/o09670103056371318321.5625818402836282329/od6/private/full/R1C1/11ip" rel="edit" type="application/atom+xml"/><atom :title type="text">A1</atom><atom :updated>2008-04-03T08:32:39.842Z</atom><atom :content type="text">100</atom><gs :cell xmlns:gs="http://schemas.google.com/spreadsheets/2006" row="1" col="1" inputValue="100"/>
contentType: application/atom+xml
uri: http://spreadsheets.google.com/feeds/cells/o09670103056371318321.5625818402836282329/od6/private/full/R1C1/11ip

Tomblooハックス – ポスト先にはてなダイアリーを追加する

Tombloo 0.0.10.1をベースに、LinkとQuoteをはてなダイアリーにもpostするやつを作ってみました。

tombloo@brasil.to/chrome/content/library21_HatenaDiary.jsをコピーして、30_Tombloo.Service.jsにちょこっと下の行を付け加えて、あとabout:configextensions.tombloo.posterFilter(Tumblr|HatenaDiary)にしたら動きます。

--- library/30_Tombloo.Service.js       2007-12-04 00:01:04.000000000 +0900
+++ /Users/kuma/tombloo.lib/30_Tombloo.Service.js       2007-12-21 22:50:56.000000000 +0900
@@ -115,6 +115,12 @@
                                return FFFFOUND.post(params);
                        }
                },
+               HatenaDiary : function (ctx, params) {
+                       if( params.type != 'link' && params.type != 'quote' )
+                               return succeed();
+
+                       return HatenaDiary.post(params);
+               }
        },

        extracters : {

つくりかた

Tomblooの対応サービスを増やすのはかんたんに書けます。

クラス名を決める
クラス名はファイル名と結びついてるみたいです。今回はHatenaDiaryという名前にしたので、作るファイルの名前は21_HatenaDiary.jsでした。数字の部分は読み込まれる順番を制御しているだけなのでなんでもokです。
Tombloo.Serviceに追加する
Tombloo.Service.postersにクラス名でメソッドを作ります。サポートしているtype(Tumblrでいう7つのコンテンツの種類です)じゃないのがきたときにはsucceedを返して、それ以外のときはpostメソッドを呼びます。別になんでも呼べますが慣習としてpostなのでpostにしておくのがいいでしょう。
postメソッドを書く
postメソッドにはtypeに応じてパラメータがくるのでそれをHTTPでPOSTするデータに変換してdoXHRを呼ぶだけです。ここでも慣習として

HatenaDiary[capitalize(params.type)].convertToForm(params);

というかたちで変換するのがいいでしょう。

タイプごとのconvertToFormを書く
typeに応じてどういうデータをpostするかを書きます。HatenaDiary.Quote = { convertToForm: function (m) { .... } } こんなかんじでサポートしたいtypeぶんだけ書きます。

これでできあがり。

かんそう

対応サービスを増やすのは非常に簡単でいいかんじになってます。常にextensions.tombloo.posterFilterに書いたサービス全てに反映されてしまうのがやや不便ですが、そこはUIで解決できると思います。postするtypeを選んだあとにもう一段そのtypeをサポートしているサービスからひとつを選ぶメニューをつけるとか、あらかじめ7つのタイプ * サービスのマトリクスをつくっちゃうとか。

こんなかんじ、っていうのがさらっと書けていいかんじでした。

ブログがウェブページの構造を規定したことで、それを作るためのツール、プラットフォームができて便利になったのと同様、コンテンツを7つ(7つじゃなくてもいいんだけど)に規定することでなにかが見えそうな気がします。出てくる問題が1-1/N-1/N-Mの関連付けをどうするか(ひとつのエントリでひとまとまりの曲を紹介したいときはどうするのかとか)、DBMSで出てくる問題と似ている気がします。

Scissors, Fools, Tools

このサービスはこういうふうに使うもので、そういう使い方は間違っている、正しくない、みたいなのを見かける。ひらめいったーで、それ用に作られたツールはないけれど、これをこうつかえばそのまま代用できるじゃん、みたいなのを見かけたりする。

たいていのサービスは、つくったひとがこういうことがしたい、というのを元に機能やみためがデザインされている。Creating Passionate Users: Making happy usersに書いてあった

“Make the right thing easy and the wrong thing hard.”

というフレーズそのままで、そのサービスが想定している使い方であれば、かんたんに使えるし(想定している使い方をかんたんにできないのならそもそもデザインがおかしい)、想定していない使い方だとかんたんにはできないことが多い。

想定されていない使いかた

でも、たいていのサービスは、想定されている使い方以外の使い方ができる。カレンダーの予定欄に日記を書くことだってできるし、ブログにひたすら写真だけをアップロードして写真倉庫にすることだってできる。

想定されていない使い方をするのはかっこいい。想定されていない使い方をしているのに、いちばんはじめに出会ったのは8年くらい前だろうか。とある日本のサイトに設置されているチャットに、なぜかドイツの人たちが入り浸ってドイツ語でチャットしまくっていた。あるツールが設置されている理由を完全に無視して他の使い方をすることができるというのにそのとき気がついた。

2chくわしくないのだけれど、2chでスレにしおりを入れるというのも、かっこよかった。

 −−−−−−−−−−−−−−−−−−−−−−−−−−
俺様用しおり
 ∧_∧
( ・∀・)< 今日はここまで読んだ
−−−−−−−−−−−−−−−−−−−−−−−−−−

みたいなカキコをする。ほんとにしおりとして使うためにカキコしてるわけじゃないだろうけど、みんなで使うためのものを自分だけのために使うという考え方に、ドイツ人がチャットを占拠しているのとおなじものを感じた。

そのツールを作ったひとが想定している使い方をばっさり無視して、自分の好きなように使うのはかっこいい。

メッセンジャー

ある日、自転車メッセンジャーのスタイルを読んで

無鉄砲で危険で、時には法律違反だって思われるような走りで沢山の人をイライラさせたりしてたら…ほら、これって若者の崇拝するアイコンの持つ条件がほとんど揃ってますよね!(笑)

の部分で人間の本性を考えるに似たようなことが書かれていたのを思い出した。

男性は痛みによく耐え、地位や注目や、そのほかのあやしげな報酬のために生命の危険をおかそとうする意欲も強い。「崇高なまでにばかげた方法でみずからを遺伝子プールから抹消し、人類の長期の存続を確実にした個人」に毎年贈られるダーウィン賞は、ただでコーラを手に入れようとして販売機を傾け、その下敷きになってしまった男、だれがいちばん強く対戦者地雷を踏みつけられるかを競った三人の男、気象観測用の気球を結びつけた芝生用のイスに座って地上二マイルまで飛びあがり、海まで流されたパイロット志願者(彼はヘリコプターに救助されたため、選外賞しかもらえなかった)などである。

人間の本性を考える(中)124ページより。

ちょうどこの本を読んでいたときに小学生が閉まる防火シャッターをくぐって遊んでいて挟まれる事故があったので、よく覚えている。でも、小学生の男の子的には確実に防火シャッターが最も閉まっている状態でくぐり抜けたやつが当然偉くて賞賛の的になるのは当然だ。今の自分でも共感できる。自分も階段を何段飛ばしで飛び降りれるか、みたいなのをあやしげな報酬のためにやった。

大人になると、そのあやしげな報酬がどれくらい実体のあるものかを判断するようになるけれど、それでもやっぱり心の中にはそういうあやしげな報酬のために危険をおかそとうすることをかっこいいと感じる部分はそのまま残っている。馬鹿げたことをしているという目で眺めつつも、賞賛を贈らないではいられない。

Napsterは、圧倒的に便利だったというのもあるけれど、それだけではなくてインターネット上に無鉄砲で危険で、時には法律違反だって思われるような小学生男子的価値観と照らし合わせて”かっこいい”ものをはじめて(自分の知る限り、だけど)みせてくれたところにしびれたのかもしれない。

マッシュアップ

そんな小学生男子的価値観でいけば、サービスをサービスが想定している方法で使うことほどださいものはない。nisshi.yugop: About Samplingで書かれている

所謂マッシュアップ(死語)における引用元/引用先の関係としては、こんな風に引用元を全くもって粗末に使い倒し、美味しいとこだけおんぶに抱っこしつつ、ひょいっと違うレベルに組み替えてしまう、てのが最高なんだと思う。よくある「とりあえずマッシュアップしてみました!えへ!」的なサービスって、「お前らGoogleとAmazonの奴隷か」みたいな感じが否めないんだけど、このサービスぐらい「美味しいとこだけイタダキマース」感が強いと、 YouTubeの中の人、ちょっとイラっとしそうだもん。

の部分には、上に書いた小学生男子的価値観でかっこいい!というのがあるんじゃないかと思う。用意されたAPIを、相手の意図とは違う、世の中の作法を無視した方法でもって使うからかっこいいのだと思う。

沢山の人をイライラさせたりあやしげな報酬のために才能の無駄遣いをすること、それは昔に男子小学生だったことがあるひとならどこかにやっぱり今でも持っている評価基準なんじゃないかとこのごろ思う。

LDRizeの(一部だった)minibuffer用にシェルを組んでみた

あっというまにLDRizeが新しくなって、それもLDRizeのminibufferにコマンド追加してみたこと雑感で書いたことまでとりいれていただいて感謝です。

それにちゃんと追いつけてないながら XUL Apps > Tips > 選択範囲のリンクを収集する 〜 DOM2 RangeのcompareBoundaryPointsの使い方 – outsider reflex を見て、これをLDRizeでやりたい!と思っていたのでminibufferにかんたんなシェルを作ってパイプで繋げてあそべるコマンドを作りました。

minibufferが分離されて単独で動くようになったのは知っていたのですが、間抜けなことにいちどALt-xを押してみてあれなんか動かない(新しくしてなかったのかも)、と思ってからとりあえずいいやと思って8.24版のLDRize.addCommandのアプローチで実装しちゃいました。

デモ: twitterで囲んだ範囲のstatusページだけを開く

minibufferがブラウザ上で動くというのの利点は、マウスでちょこちょこっと操作するかんたんさと、細かく複雑な指定ができるコマンドラインがいっしょになっているところだと思います。囲んだ範囲のリンクをみんな開くみたいなのはブックマークレットでふつうにできて芸がないので、twitterのページでマウスで囲んだ部分のうち、発言のperlamlinkになっているリンクだけを開く、というのをやってみました。

まずはマウスで好きなだけ囲みます。

こんなかんじ。

そして : を押してminibufferを開きます。

スクリーンショットをとったときには selected, links, grep, openintab の4つのコマンドを作ってました。下の方にリンクしてあるスクリプトはさらにimages, xpath, echoを追加してあります。

シェルのコマンドラインと同じかんじでコマンドを入れてパイプで繋いでいきます。

Command Piped

selected は、マウスで選択されている範囲のノードを返すコマンドです。そのあとにパイプで繋がれているlinksコマンドは入力されたノードの中に含まれるAタグだけを取り出して返すコマンドです。
あ、ちなみにまだてきとう実装なのでselected以降のコマンドはLDRizeのコマンド補完機能は効きません。
そのあとにあるgrepコマンドは渡されたノードのリンクに引数で指定された正規表現が含まれているものだけを返します。今回はtwitterの発言permalinkだけを取り出したいのでpermalinkだけに含まれているstatusesを引数に渡しています。
最後のopenintabコマンドは、渡されたリンクをぜんぶ新しいタブで開きます。

タブがたくさん開いたところです。

わかりにくいのでヒストリで見てみます。

はじめにマウスで囲んだ範囲に含まれていた6つのpermalinkが開いているのがわかります。

今回はAタグだけを取り出すlinksを間に入れましたが、IMGタグだけを取り出すimagesを間に入れてもいいですし、取り出すものをxpathコマンドでXPathを使って指定することもできます。
間に入れるフィルタが作れるだけでなく、minibufferのコマンドはjavascriptで好きに書けるので、最後に画像をreblogしたりブックマークしたりjavascriptでできることなら何でも可能で、しかもちょっと動作をかえたいときにはコマンドにちょっと引数を渡せるので細かい用途ごとにべつべつのブックマークレットを用意しないといけないみたいなのもなくなります。minibuffer最高!

スクリプト

というわけでよかったらためしてみてください。

実装アプローチ

ldrize.user.js, minibuffer.user.js本体に少し手を入れないと実現できなかったのですが、とりあえずLDRizeのほかの部分と整合性がとれているか何も考えずに手を入れました。

まずminibuffer.user.jsの、minibufferでEnterを押したときに呼ばれるbindDoAndExitをいじりました。minibufferからコマンドラインに入れられた文字列を作った Minibuffer.__Shell.parse() に渡してパース、実行するべきコマンドのリストを得てます。

コマンドを実行するときに、コマンドの引数と、シェルでいう表示ユン入力を渡したいので引数を二つ増やしてます。

@@ -174,11 +195,18 @@
      Minibuffer.updateComplationList();
   },
   bindDoAndExit : function(){
-     var cmd = Minibuffer.getCandidateCommandHash()[Minibuffer.input.innerHTML];
-     if(!cmd) return;
-     var str = Minibuffer.input.innerHTML;
+     var commands = this.__Shell.parse(Minibuffer.input.innerHTML);
+     //FIXME: check command existence first, and  exit and execute them all.
      Minibuffer.exit();
-     cmd(str);
+     var stdin = null;
+     var ret = commands.forEach( function ( command ) {
+       var cmd = Minibuffer.getCandidateCommandHash()[ command.name ];
+       if(!cmd) {
+           return null;
+       } else {
+           stdin = cmd(command.name, command.args, stdin);
+       }
+     } );

minibufferでcmdとして呼ばれた関数はLDRize.minibufferCallbackなので、こっちも引数を増やします。stdinが空のときはLDRize側でセットされているピンのリストを渡します。

コマンドが返す値はtrue/falseでLDRizeでセットしたピンをクリアするかそのままかの指定に使われていますが、これをそのまま流用してコマンドのフィルタ結果を返すようにして、さらにreturnを追加してminibuffer側に結果を戻します。

こうすると本来のピンを外す、外さない機能と鑑賞しそうですが、ふつうminibufferの最後に実行するコマンドはopenintabのようなフィルタ結果を返さないコマンドなので、それがピンを外すか外さないかの本来のLDRizeのコマンドとしての値を返してあげればだいたい問題ない気もします。

@@ -981,14 +981,17 @@
      for (x in LDRize.command) obj[x] = LDRize.minibufferCallback;
      window.Minibuffer.complete(obj, LDRize.minibufferCallbackExit, LDRize.PROMPT);
   },
-  minibufferCallback : function(string) {
+  minibufferCallback : function(string, args, obj) {
      var lst = LDRize.getPinnedItemsOrCurrentItem();
-     var obj = {
+     obj = obj || {
        paragraphes : lst,
        links: lst.map(LDRize.getLinkURL),
      }
+       obj.args = args;
+
      var continuePinList = LDRize.command[string](obj); // do command
      if(!continuePinList) LDRize.clearPinlist();
+     return continuePinList;
   },
   minibufferCallbackExit : function(){
      document.addEventListener('keypress', LDRize.handleKey, true);

ちなみに、一番はじめに書いたコマンドには入力としてピンを立てたノードのリストがくるので、

images | echo 

とすると、ピンを立てたパラグラフの中に含まれる画像のノードとURLがFirebugのコンソールに出力されます。

思ったこと

minibufferすごすぎると感動しながらシェルをつくって思ったことをいくつか書きます。

JSONストリームのエディタ

LDRizeはminibufferのコマンドを呼び出すときに、関数の引数として

{
    paragraphes: array_of_domNodes,
    links: array_of_strings_contain_url
}

という、DOMのノードのリストとそれに結びつけられたURLのリストを渡してきます。
linksgrepのようなコマンドは、まさに かんたんすぎ かっこよすぎ Yahoo pipes にちょっと書いてた IBM dW : XML : XMLの論考: マイクロフォーマットのパイプストリーム – Japan と同じな感じがしました。minibufferはブラウザ上でLDRizeのピンのようなKUI(Keyboard User Interface)とGUI(マウス)で抽出したデータをJSONで処理するかんじです。

JSONを処理するパイプ、という気持ちでコマンドを書いていていまのLDRizeの仕様で気になったのが、コマンドに渡されるデータがオブジェクトのプロパティにArrayが入っているところでした。JSONデータを加工するパイプとしてコマンドを捉えると、プロパティのはいっているオブジェクトのArrayが渡されるほうが便利でした。

前者だとgrepの実装がこう

        name: 'grep',
        command: function (obj) {
            var regex = obj.args.shift();
            var modifier = obj.args.shift();
            var re = new RegExp(regex, modifier);
            var paragraphes = [];
            var links = [];
            for ( var i = 0; i < obj.links.length; i++ ) {
                var u = obj.links[i];
                if ( u.match( re ) ) {
                    paragraphes.push( obj.paragraphes[i]);
                    links.push(u);
                }
            }
            obj.paragraphes = paragraphes;
            obj.links = links;
            return obj;
        },

なってforでループさせて再度Arrayをつくるというのをせざるをえないですが、データが後者のようにオブジェクトのArrayになっていると

        name: 'grep',
        command: function (obj) {
            var regex = obj.args.shift();
            var modifier = obj.args.shift();
            var re = new RegExp(regex, modifier);
            return obj.filter( function ( item ) {
                return item.link.match( re );
            }
        },

でできるのですごく自分がかしこくなった気分になれそうです。LDRizeのピンを設定する側の処理のところはコードをよく見ていないのでそっち側で何か問題があったりするかもしれないですが、こうしていただけるとコマンドを作るときにはすごくかっこよく書けるようになりそうですー。

コマンドでのプロパティ追加

LDRizeからピンのデータとしてコマンドに渡されるデータはノードとそれに対応するURLだけど、途中のフィルタで

{
    paragraph: domNode,
    links: url,
    name: ....,
    reblog_id: ....,
}

みたいに新しくプロパティを追加したオブジェクトを結果として返すと、それより後ろのコマンドでそのプロパティを参照できる、というようにしておくと自由度が広がるのかも。自分が書いたものはそうなるようにしてみました。特に何にも使ってないですけど。

chrome特権

やっぱり最後に saveto localdisk とか saveto flickr とか reblog とかしたくなるのでminibufferだけextensionにしてコマンドでディスクに書いたりしたいです!minibufferがLDRizeと別で単体でも動くように変わったおかげでちょっといじればできそうな気がするのでこれはそのうちやってみようとおもいます。
特権が必要でかつ必要そうなのはいまのところsaveto localdiskくらいしかおもいつかないし、ほかはGreasemonkeyでがんばればできるので、それだけのためにセキュリティリスクとインストール後に再起動しないといけないお手軽でなさ(Fx3で再起動がいらなくなるみたいな記述があったけどどうなんでしょう)を呼び込む価値があるのか微妙ですが….

感謝!

こんなにたのしいぴかぴかのおもちゃを作っていただいてありがとうございます。前回は出過ぎたことを書いたと反省しつつも、このポストがなにかしら今後の設計の参考のお役に立てればと思って今回も書いてみました。

LDRizeのminibufferにコマンド追加してみたこと雑感

LDRize(2007.8.24版)のミニバッファにreblogコマンドを追加するスクリプト LDRize_tumbler_dashboard_reblog.user.js を作ってみて感じたことをべつでこっちに書きます。

とりあえず、ほんとに誰も見ていないはてなダイアリーのメモ書き程度な自分の日記にコメントしていただいてありがとうございました。びっくりました。AutoPagerizeと組み合わせたときにちょっと不便だったところのパッチを書いてお送りしようと思ってるのですが、とりあえずミニバッファ用のコマンドを作ってみていくつか気がついたことを書いておきます。

ピンはパラグラフに対してつけたい

現状(8/24バージョン)ではピンはlinkに対してつけるようになっていて tumblr dashboard のようにlinkがsiteinfoに定義されていないときにはピンを立てること自体ができないですが、ミニバッファのコマンドでparagraphもうけとれるようになったし、なにかとlinkがなくてもピンを立てられる方がべんりな気がします。(といってもlinkが定義されていないのはほんのわずかしかないですね)
ただ、linkがないものにピンを立てられるようにすると右側のピンを立てたもの一覧に表示するものがなくなっちゃうので困りそうですが….

setSiteinfoはよくない

おなじく8/24バージョンの追加機能のsetSiteinfoですが、これはよくないと思います。

まず現状では使い勝手がよくなかったです。
tumblr dashboard ではlinkが定義されていないのでsetSiteinfoで定義してピンが立てられるようにしたのですがsetSiteinfocommandの中でしか呼び出せないのでsiteinfoを変更するためだけにミニバッファからコマンドを呼び出す必要があって不便でした。ミニバッファにコマンドを追加するスクリプトがロードされたときに書き換えられるようにwindow.LDRize.setSiteinfoで呼び出せると使いやすくなると思います。

ですがsetSiteinfoはXPathをスクリプト本体に書くことになる点で好ましくないと思っています。
siteinfoのよいところは、スクリプトのコードかせページのスクレイピングのためのXPathが分離されたところだと思っています。ページのスクレイピングルールがコードから分離されて誰でも変更できるようになったことで

  • スクレイピングルールを他のスクリプトや異なる言語からでも利用できる
  • ページのデザインが変わってルールが壊れても、誰かが直してくれれば他のひとは壊れたことにも気づかないで使い続けられる
  • ページのデザインが変わるたびに新しいXPathを記述したスクリプトに入れ直す必要がない

という利点ができました。
setSiteinfoでコードの中にXPathを書いてしまうと、これらの利点が全部無くなってしまうのでsetSiteinfoでスクリプト固有のXPathを保持するよりも、それらもsiteinfoに混ぜてしまった方がよいと思います。siteinfoのサイズは大きくなりますが、あるスクリプトで使っているXPathを他のスクリプトでも参照したい、ということがあるでしょうし、それらがべつべつにメンテされるようになっちゃうのはもったいないと思います。

LDRizeでピンを立ててparagraphに対してコマンドを実行できるというのは、なんかFirefoxがOSでその上のシェルでコマンド実行しているみたいな感じがしました。この比喩で言ったらsiteinfoはディスクからビット列を読み出してきて、それを意味のあるかたちに組み立てるデバイスドライバみたいなものなのかなと思います。みんながそれぞればらばらにデバイスドライバを書くよりは、誰かが書いたのをみんなが使う方がハッピーだと思うのでsetSiteinfoではなくsiteinfoに項目を増やす方向で対応してほしいです。

そのほか細かいこと

siteinfoに項目を増やす、というのと同じはなしですが tumblr dashboard のようにlinkの中が画像だったりすることもあるのでURLを取り出すlinkと右側に表示するテキストをべつべつに記述できるようにしてもいいのではないでしょうか。デフォルトlinkのcontentTextで、ルールが記述されていればそれを使う、というような。

あとwindowのloadイベントではなく 実用 – ドキュメントのロード完了に合わせて関数を実行する に書かれているdocumentのDOMContentLoadedで実行されるほうが早く使えるようになるので便利だと思います。自分で書き換えてみたらちゃんと動かせなかったのでできるのかよくわかりませんけど….

感謝!

なんか文句ばかりになってしまいましたが、コマンドを好きに作って何でもできるというのは、ブラウザがOSに近くなったかんじでとても楽しいです。しかもそんなエッジなものにぴかぴかのみためがついていてほんとに感動しました。LDRize上でもっといろんなことができるようになるように、さらに自分もなにかつくっていこうとおもいます。

Alexa toolbarのユーザ統計

FC2、mixiを抜いて国内3位の視聴率に–Alexa調べ:ニュース – CNET Japan を見て、こないだの Alexaツールバーがクライアントにインストールされているかを検出する方法 を思い出したのでてきとうに集計してみました。

このサイト(ido.nu)のアクセスログ30日分を単純にgrepしました。母集団に偏りがあるので厳密に調べたところで意味がないので、だいたいこれくらいっていうオーダーがつかめればいいかなと思いますです。ログの行数が507,025に対してAlexaを含むものが551なので、単純に計算したら0.0010%でした。これだけなのもあんまりなので自分が見られるほかのサーバのログも見てみたけどやっぱり 0.00011 ~ 0.0020 % でした。

サイトによっては違うと思われますが、使っているひとはほとんどいないという認識は間違ってなさそうです。でも、統計的にはテレビの視聴率調査並みのカバー率はあるから十分有効なのかな。うーん….

あ、あと今となってはどうでもいいけど Alexaツールバーがクライアントにインストールされているかを検出するスクリプト の方法だとUAにはAlexa Toolbarと入っているのにCreateObjectできていないものもあったので、バージョンによって取れたり取れなかったりするのかもしれません。ActiveXのon/offの影響もあるのかも。

Alexaツールバーがクライアントにインストールされているかを検出するスクリプト

ブログとSNSのいいトコ取り!? ミニブログにハマる人が増えている:デジタルARENA を読んで思い立つ。

サイトのトラフィックを手軽に知ることができるものがalexa以外にないので、Alexaでだいたいの数を知るしかないけど、日頃Alexaのツールバーをインストールしているひとをみたことがない。だからあんなの信用できないよね、というのも進歩がないので、どれくらいのひとがインストールしているものなのか調べることにしました。

自分はWIndows時代ずっとVisual C++でそのあとCOMで挫折したので、どうやったら調べられるのか全く分かんなかったのでどうしたかについてから。firefoxは navigator.plugins でやればできるので(むしろextensionなfirefoxが検出できなさそう)問題なのはCreateObjectしないといけないIEです。

まず 戦争 – COMの調査、オブジェクトブラウザ を参考にExcelで捜索。手元のExcelでは[プロジェクト/ライブラリ]というのが存在せず、F2で出てくるオブジェクトブラウザで選ぶようになっていました。ALXTBLibというそれっぽいものがあったものの、これをCreateObjectすることはできませんでした。

それで思い出したのが[インターネットオプション][プログラム][アドオンの管理]でIEのブラグインのon/offが管理できたこと。

Alexa

Excelの Visual Basic Editor でヤツのファイル名がAlxTB1.dllだと判明していたので狙いは

  • AlxTB BHO Class
  • BrowserProxy4 Class
  • Menu Class

の三つに絞れました。
これはどう見ても human friendly な名前なので、内部的な名前をどこかから見つけてこないといけません。たぶんレジストリを検索すれば見つけられると思うけど、手元のマシンには Windows Platform SDK が入っていたので、その中にある OLE-COM Object Viewer というのを使いました。

あとはもう当てずっぽうなんですが Object Classes/Grouped by Component Category/Automation Objects/ の中から上の3つのクラスを見つけてそのprogIDを入れてCreateObjectしていきました。そうしたら PopMenu.MenuCreateObjectできるのを発見。

Comview

というわけで、現在

<script type="text/javascript" charset="utf-8">
var is_alexa_present = 0;
</script>
<script language="VBScript" carset="utf-8" >
On Error Resume Next
is_alexa_present = 0
is_alexa_present = (IsObject(CreateObject("PopMenu.Menu.1")))
</script>
<script type="text/javascript" charset="utf-8">
window.is_alexa_present = is_alexa_present;
// <![CDATA[
//o = (IsObject(CreateObject("CnsHelper.CH.1")))
if ( document.all ) {
        String.prototype.qq = function () {
                return '"' + this.toString() + '"';
        }
        var u = "http://alexa.ido.nu/" + ((is_alexa_present) ? "1" : "0") + ".png";
        document.write( '<img src=' + u.qq() + ' />' );
}
// ]]>
</script>

というコードでAlexaツールバーが入っているかをトラッキング中。(対象がIEだけだけど)

あとおまけだけど CnsHelper.CH にすればJWordを調べられます。

tumblrのreblogで消えるもの

けっきょくWeb2.0ってなんだったの に書いてたのとおんなじことだけど、つづけざまに二つ読んで、昔に死んだ猫が不憫でならないのでこれを書く。

MouRa|日本、課題|yomone!SP » ネット幸福論 第九回

また、Tumblrで独特なのは「reblog」という概念だ。これは他のユーザーの記事を自分の記事に取り込んでしまうことで、reblogがどんどん連鎖していくと、誰が最初に記事を書いたのかわからないという不思議な状況が生まれる。Tumblrに掲載されているコンテンツは、そのユーザーが作成したものとは限らないのだ。だからこそ、「個の喪失」とも言うべきものがTumblrの本質のようにも思える

Twitter / Kouichirou Eto: やっぱり,Tumblrの本質には「主体の消滅」があると…

やっぱり,Tumblrの本質には「主体の消滅」があると思うな.これを抜きにしてはTumblrは語れん.

ueBLOG |tumblrにどっぷりはまった2ヶ月 にある

自分が尻フェチだと思った事はこれまで全くなかったのですが、ReBlogしてる画像を見返してみると意外と尻ものが多かったので思わず発言しました。

という事実で分かるようにtumblrでも、そのtumblelogにあるひとつひとつのエントリにアイデンティティを見出すことはできないけれど、そのtumblelog全体としてみたとき何が入っていて何が入っていないかに tumblelog author のアイデンティティが現れてくる。

自分自身が作り出すものこそが自分自身だと考えるのなら、大量生産技術の発達でもう自分の手では何も作ることができなくなっている我々にはもはや自分自身なんて存在しないことになる。けれど、実際のところ我々はその大量生産されたもののうち、なにを自分のものとして手に入れて、何を自分のものとして手に入れないかによって自分自身をかたちづくっている。何十年も前から。

tumblrのreblogが、”個の喪失”とか”主体の消滅”という印象を与えるのは、その昔金物屋さんといったら自分でやかんをたたいて作るひと、だったのが、大量生産されたやかんを売るひと、に変化して行ったタイミングで、自分のやかんを作りだす金物屋としてのアイデンティティの喪失をおそらく感じたのとおんなじだ。
いまtumblrで、ブログといったら自分で何か考えて書いて読んでもらうもの、という時代から、ちょこっとreblogしてpublishして人に見てもらう、という大量生産時代に移り変わっている。この状況に、自分の持っていたブログの生産技術がInstant, no-overhead tumblelog で大量生産されるエントリに圧倒され、とってかわられるところに、アイデンティティの喪失、つまり”個の喪失”や”主体の消滅”を感じているのかも。

Google Web History 活用事例 – ダウンロードした画像の出所を探す

ウェブを見ていてちょっと好きだった画像をローカルに保存しておいて、画像のアップロードテストの時とかに使うことにしている。そうすると画像をアップロードしたりするのが少し楽しくなるから。

気がつくとデスクトップに compiler って書いてある画像が置かれているけれど、どこのページにあったものなのかがぜんぜん思い出せない。

Compilerbanner

コンパイラの話が書いてあるようなブログだったらチェックしたいなと思って思い出そうとしても思い出せない。spotlightを使っているとspotlight情報のところにダウンロード元のURLが書いてあると聞いたけれど、オフにしているからか書かれていない。ファイル名でイメージ検索してみても出てこない。あとはファイルの最終更新日時だけ。

でも Google Web History を使っていたらこれで十分だった。更新日時は 5/19 16:53 なので、それより前に見たウェブサイトがわかればそのどこかからダウンロードしてきているはず。というわけで履歴を探すと簡単に見つけることができた。

4:54pmにPopflyを検索してWIREDのCompilerというところに書かれていた Microsoft PopFly Looks Like Yahoo Pipes On The Xbox を見て、バナーが気に入ってダウンロードしたんだった。

というわけで、データが全部残っていると意外なところで役に立ったりします。

twitterユーザ情報データ

ネコが体調を壊してみんなに迷惑をかけたりしないかと思いつつ、先日来体調も良さそうなのでクローラをしかけて 自分のアカウント から friends になっているユーザづたいに friends of firends まで、そのひとの friends リストを取得しました。

自分のfriendsが 116 そのfriendsが 4541 そのfrindsが 34227 になりました。

同じユーザをfriendとして登録していた場合は、自分から見て近い関係のところでのみ数えています。自分のfriendsの4437ユーザでの、平均friends数は53.3でした。
Twitterの利用方法および利用者の意識調査 – 人力検索はてな と比べるとはてなのアンケートは圧倒的にfriendsが少ないかんじ。はてなアンケートのユーザは相対的にtwitterになじめなかったということなのかな。

自分のfriendsの数に平均をかけると 116 * 53.3 = 6182.8 で、実際の数字は 4541 なので差の1600はほかのユーザと同じひとがfriendsになっていることになります。それに対して friends of friends だと 4541 * 53.3 = 242035.3 で差は10000くらい。
friendsだけだと大半が日本人で、 friends of friends になると大半が日本人でなくなるのですが、どちらでもfriendsの重複割合はあまり変化してないかんじ。

自分ではどうデータを切り取ったらおもしろいのかとかよくわからなかったのでデータは twitteruserdata20070520.bz2 に置いておくのでおもしろい情報を引き出してもらえるとうれしいです。サイズは2.7Mくらい。

テーブルはこんなかんじになっています。

CREATE TABLE `friends` (
  `user_id` varchar(64) NOT NULL default '',
  `friend` int(11) NOT NULL,
  PRIMARY KEY  (`user_id`,`friend`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL,
  `screenname` varchar(64) NOT NULL,
  `name` varchar(255) NOT NULL,
  `status` enum('crawled','notyet','pending') NOT NULL default 'notyet',
  `depth` int(11) NOT NULL default '1',
  `image` varchar(255) default NULL,
  `protected` varchar(32) default NULL,
  `url` varchar(255) default NULL,
  `location` text,
  `description` text,
  `friends` int(11) default NULL,
  PRIMARY KEY  (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

wakoopa – ソーシャルソフトウェア使用ログシェアリング

MoMBで知ったWakoopaは、自分のマシンで動いているアプリケーションのログをとってサーバに送って保存するという、ロガーにはたまらない機能に、”おんなじアプリケーション使ってるともだち”がわかったり”これをつかっているからこれもおすすめ”するという、ありがち、というか王道真っ正面なソーシャル機能がくっついたもの。ソフトウェアでソーシャルといえば iusethis mac software っていうのもあったけど、見た目の楽しさ(iusethisサイテー)とハイテクさ加減、名前のへんなかんじでwakoopaの勝ち。

ちょっと前にロガーの間でちょっとの間ちょっとだけ話題になったOSX用の slife と違って、任意のアプリケーションのログをとってくれます。slifeはなぜか対応しているアプリケーションしかログをとってくれなくて、しかもなんか記録されているデータが正しくないかんじで”すべてを記録したい”というロガーのココロをわかってくれないツールで使えなかったのですが(OSXでしか動かないし)、今度のwakoopaはWindowsで動くのはもちろん、きのうOSXのクライアントもリリースされました。昨日はなんかちゃんとデータが送られてないのか、サーバのプロフィールにOSXのソフトウェアが出て来ませんでしたが、今日はちゃんと表示されるようになりました。Firefoxが二つあるのはWindowsとOSXとでべつべつに認識されて表示されているからです。

とりあえず登録してみたらtwitterでfriendsになっているひとがいたのでtwitterとおんなじのりで自分のcontactにaddさせてもらいました。そして数日後。wakoopaをもう一度見に行くと、あたりまえなのですが、よく知らないけどこのidは知ってる、くらいだった twitter friends が使っているアプリケーションのデータがサーバに蓄積されて、普段何を使っているかがわかるになっているのです。

ついったー部で有名な twitter mobile を作っている(使わせてもらっています!)あの人は普段こんなアプリケーションを使っている、というのがわかるわけです。
ソーシャルブックマークで何をブックマークしているかで何に興味があるひとかが見えてくるけれど(What you are is what you bookmark)、ああこのひと普段こういうことしてるんだっていうのがわかって(What you are is what you use)より親近感がわいてきました。きのう地下鉄駅で “you are what you buy” ってコピーのポスター見たけど

そのソフトウェアを起動している時間順で並ぶのて、上の方は自然とブラウザだったりメーラだったりメッセンジャーだったりでつまんないのですが、ところどころに聞いたことのないマイナーアプリが混じっていたりして、そのツールなに?というかんじで自分が知らなかったツールを知れるのがおもしろいです。リリースされたばかりのツールならともかく、昔からあってみんな当たり前のように使っているツールだと話題にも上らなくて、えーそんなのあったの!みたいなことにたまになったりするけど、そういう発見ができそう。なので使うひとが増えるといいなと思います。みんなTeraPad使ってるんだな、とか。どうでもいいけど自分サクラエディタです。デスクトップ百景に通じるところがあるかも。デスクトップ百景がWeb1.0的文芸作品だとしたらwakoopaはWeb2.0のMachine-to-Machineで生成される統計データ。

日本語でwakoopaを検索すると一番上に出てくる、スーツのおっさん向けウェブ業界ニュースサイトのTechCrunchには

所詮デスクトップ用アプリケーションなので音楽のようにマス市場にはアピールしないだろう。誰もが飛びつく無料ソフトをいくつかトラッカーにバンドルしてやらないと、ディベロッパーの小さなコミュニティから抜け出せない、そんな気もする。

なんて書かれちゃうけど、ひっくりかえせばデベロッパーの小さなコミュニティには楽しいツールになるってことでは。

Google History のRSS – 履歴とその検索結果

昨日の 見たページぜんぶの履歴が残せて、その履歴だけからGoogle検索できる Google History ですが、今日履歴を見てみたら

こんなかんじに。
ブラウザの履歴と同じ情報が載っているだけですが、見た時間とページのタイトルがセットで表示されていることで”ログをとっている”感がでてきてたまりません。ログを見ればその日いちにち何をやっていたかが一目瞭然です。

そうなると、このデータをもとに何かしたくなるのが人情というもの。やろうと思えばローカルのヒストリファイルから File::Mork なんかを使って読み出して加工するなんて前からできたことですが、時系列でデータを見るとなぜか楽しくて、楽しいとわかるとなにかやりたくなるのはロガーの血なのでしょうか。

で、すごいことにそんなひとのために、この見たページの履歴がRSSで用意されています。
ちゃんとgoogleのヘルプページの Can I get an RSS feed of my web history? に書かれています。
googleのアカウントにログインするときの認証情報を使って https://www.google.com/history/?output=rss にアクセスすると、アクセスしたページのヒストリをRSS2.0で取得することができます。
wgetでやるならこう。

wget --http-user='username@gmail.com' --http-passwd='password' \
  'https://www.google.com/history/lookup?hl=en&output=rss'

curlはデフォルトではリダイレクト先を取得しにいかないので、オプション -L をつけて

curl -L --basic --user 'username@gmail.com:password' \
    'https://www.google.com/history/lookup?hl=en&output=rss'

で取得できます。

生の閲覧履歴がRSSで取れるだけでなく、履歴を特定のキーワードで検索した結果のRSSも取得することができます。

wget --http-user='username@gmail.com' --http-passwd='password' \
  'https://www.google.com/history/find?hl=en&output=rss&q=perl'

にすると、履歴の中からperlが入っているものだけのRSSが取得できます。ごちゃごちゃするので省略してますが、日付で範囲を指定して検索することも可能。

下がperlをキーワードにして自分の履歴を検索してみた結果です。

  <channel>
    <title>Google - Web History - search results for perl</title>
    <link>http://www.google.com/searchhistory/</link>
    <description>Google - Search History RSS feed</description>
    <item>
      <title>WWW::Mechanize SeleniumIDE API - bits and bytes</title>
      <link>http://labs.gmo.jp/blog/ku/2007/04/wwwmechanizeseleniumideapi.html</link>
      <pubDate>Wed, 25 Apr 2007 2:42:39 GMT</pubDate>
      <category>browser result</category>
      <description> perl ...</description>
      <guid>H8AuRqHzFICAgLAPAA</guid>
    </item>
    <item>
      <title>Gisle Aas / URI - search.cpan.org</title>
      <link>http://search.cpan.org/dist/URI/</link>
      <pubDate>Wed, 28 Dec 2005 1:18:28 GMT</pubDate>
      <category>web result</category>
      <description>URI::snews. URI::ssh. URI::telnet. URI::tn3270. URI::urn. URI::urn::isbn. URI::urn::oi
d. hosted by perl.org, hardware provided by Online Shopping Shopping.</description>
      <guid>9PWxQ8j4OoCAgLAPAA</guid>
      <smh:query_guid>0vWxQ9__GIr2owLy3735Dg</smh:query_guid>
    </item>

日本語は苦手みたいで、日本語の入っていたところは0×20におきかえられちゃいました。hl=enの副作用でしょうか。
categorybrowser result というのは、ブラウザの履歴の中でperlにヒットしたページなのを意味していて、web result のほうはGoogleで検索した履歴の中でperlにヒットしたページなのを意味しています。

うーん、googleがこういうデータを持っているのはアタマの中ではわかっていたけど、実際にこうして目で見てみるとすごい…

見たページぜんぶの履歴が残せて、その履歴だけからGoogle検索できる Google Web History

Windows2000の時代からずーーーーーっとほしかったものがついに出たのに誰も書いてくれないので自分で書く。

前からGoogleの検索履歴があとから検索できる Google Search History はあったけど、こんどの “Google Web History” はFirefoxの Google Toolbar と連携してみたページ全部の履歴をとっておいてくれて、さらにその履歴の中からだけ検索という芸当ができる。もちろん、ページの中のテキストも検索の対象に含まれます。
ということはつまり、Googleの検索で”自分の見たページだけから検索”ができるということ。あのページどっかで見たんだけど思い出せない、検索してもたくさん出てきてわからない、みたいなのは全部解決。del.icio.usにブックマークしたけど、サーバ重いし検索うまくできてるのかいまいちわかんないみたいなのも Google History で検索すれば一発解決。すごい!

ちょっと前には ウノウサーチ があったけど、しばらくしてウノウサーチを見に行ったらなくなっていました。
ほかにも Firefox extension で見たページをローカルに保存して履歴もとってくれるらしい Slogger というのもあるらしいけどローカルに保存されるのは、バックアップとかめんどくさいことを自分がしないといけなくなるので、履歴にプライバシーを感じない自分は好きになれない。

というわけで Google History でログをとる。Official Google Blog: Your slice of the web によれば http://www.google.com/history/ からいけばいいはずなのですが、どうもFirefoxだと英語版を使っていてもまだ Google History に対応していない日本語のページに飛ばされてしまうので、直接 http://www.google.com/history/welcome?hl=en にアクセスして、パスワードを聞かれるので入力して、その後の画面で “Enable Web History” を押して、Web History 機能を有効にします。

Browser

そのあと Google History 対応の google toolbar をインストールしたらできあがり。
それからはブラウザで見たページがGoogleの検索履歴のページに

Google Web History-1

こんなかんじで、検索履歴以外に見たページの履歴も表示されるようになります。

もちろん Search History で検索すればGoogleクオリティで検索結果が出てきます。しかも自分の見たページの中からだけ。

ピクチャ 2-1
こいつを5年以上待ってました。

手に入ってしまえば欲が出るもので、次はこれのソーシャルバージョンがほしいです。
ともだちがみたウェブページからだけ検索、っていうやつ。

みためがきれいなのでOSXではSafariを使っていましたが、これでもうFirefoxに乗り換えます。
No Logging, No Life!

*

Google History じゃなくて Google Web History が正しい名前っぽいので修正しました。

追記 2008.11.17

[google][webservice]GoogleWebHistoryがすごくイイよ – Hetaruの日記によると、インストールしたあとはGoogle Toolbar自体はいらないそうです。pagerankのリクエストをトラックしているみたいでpagerankを調べてくれるやつであればGoogle Toolbarでなくていいみたい。

最近ダウンロードしたもののatomフィードpodcast

話題にならなかったけど、去年アメリカのYahooが買収したものの中に Webjay – Playlist Community というのがある。インターネット上にある音楽ファイルのリンクを集めてプレイリストとして保存しておくもの。それをストリーミングで流してくれるあたりがこのサービスのすごいところらしいけれど試してないのでそのへんはよく知らない。

Democracy: Internet TV で、比較的小さなレコード会社のアーティストのプロモーションビデオをDVDレベルのクオリティで配信している Telemusicvision.com を知ったあたりから、じつは海外の比較的小さなレコード会社に所属しているアーティストの曲はけっこうフリーでダウンロードできてふつうに聞けるものがたくさんあるのを知って、そういうもののリンク集が欲しい、作りたいと思っていた。

というわけで、前に作ったSafariのヒストリを記録するPlaggerブラグインをちょこっといじって、最近Safariでダウンロードしたファイルのatomフィードをつくりました。
ダウンロードした音楽ファイル、ムービーはもちろんPDFやtarballもフィードされます。podcast対応にしたので、itunesでsubscribeしておけば音楽だけ自動的に聞くこともできます。(できるはず)
自分がダウンロードしたもののフィードは自分には楽しくないので、ぜひみなさまダウンロードしたファイルのフィードを公開してください!っていうのをさくっとできるようなのを作るべき?

gooメモリ・リトリーバβ雑感

ずーっと昔のwindowsNT4.0とかの時代からブラウザで見たページを保存しておいて検索したりしたいと思っていて、こないだsafariのログをとるやつを作ってたりしたけど、今回の gooメモリ・リトリーバβ – gooラボ はハイテクで、ITmediaによる

Webページの閲覧履歴に加え、各ページの閲覧時間や閲覧順、Web検索で入力した検索キーワード、ページのコンテンツをコピー・印刷したかどうかも記録。クリックしたハイパーリンクのテキストや、ページ内でマウスドラッグして選択(反転表示)したテキストの内容も保存

と、いろいろやってくれます。特に閲覧時間をスコアに反映してくれるのはよさそうです。どの程度反映するかのパラメータチューニングと、開いてるけど見てないのをどう区別するのかの問題がうまく解決できていれば、ローカルでのスコアリングならではの手法だし、検索精度が格段に良くなりそう。
履歴管理ツールの設定ダイアログを開くと、プラグインという項目があり、中身を見ると
Plugins-1
それぞれのPluginが収集しているデータに対応しているみたいです。ということはプラグインを作れればIEだけではなくFirefoxにも対応させられるし、safariで見てたものの履歴を入れちゃうとか妄想もふくらみますが、ソースはともかくインターフェイスも非公開なので自分でプラグインを作るとかできません。windowsでお手軽に使えるところは、自分でヒストリを hyper estraier に入れるみたいなことをするのにくらべてスマートだから、ソースとまでは行かなくともライブラリのインターフェイスが公開されていると遊べていいのにー。いつのまにかウノウサーチはなくなっちゃってるし。

最後に気になる点をふたつ。

収集したデータがサーバに送信されるのかどうかがわかりません。インストール後にショートカットが作られているマニュアルを読むと

goo メモリ・リトリーバ で蓄積された操作履歴データおよび作成されたインデックスは、ローカルPC 上で管理/利用されます。goo メモリ・リトリーバによって、利用者からの明示的な許可なく操作履歴データやインデックスがgoo や第三者に伝わることはありません。
インストール時、あるいはgoo メモリ・リトリーバ検索ツールのメニューバーで、「goo メモリ・リトリーバの機能向上に協力する」チェックボックスを選択すると、障害レポートとgoo メモリ・リトリーバソフトウエアの利用状況を示す情報((以下「利用実績」といいます)について、匿名の情報をgoo のサーバに送信します。

って書いてあったけど、こういうツールで最も気になることなので、わかりやすいところに書いてあってほしいです。

そして、どんな情報を収集しているのか、公式ページにも書いてないし、マニュアルにもはっきりとは書かれていない(公式ページよりもITmediaの記述のほうが詳しい)ので不安になってきます。インストール時には”PCの操作履歴”を保存しますとだけ出てくるだけで、どこまで保存してるのか参照することもできません。

はてなグラフAPIでディスクの残り容量を記録する

はてなグラフができたときに、これにAPIがあってAPI経由でデータが書き込めたら!と思っていたんだけど、いつのまにか はてなグラフ数値登録APIとは というのができていた。
というわけで、てはじめにディスクの残り容量を送り込むスクリプトを作りました。こいつをcronで実行して変化を見て楽しんでいます。
$disksに ホスト名 => [ グラフにしたいパーティションのマウントポイントのリスト ] というのを記述しておくと、はてなグラフの自分のアカウントのところに df:ホスト名:マウントポイント という名前ではてなグラフにディスクの残り容量を送るようになっています。
自分のグラフは、dokodemoというのとmogumoguというのとこのサーバの残り容量を10月半ばから記録しています。まじめにやるならMRTGでもいれればいいことですが、手元の開発マシンとかpowerbookとかそこまでしたくないけどちょっと見たい程度のやつで使っています。

#!/usr/bin/perl
use strict;
use warnings;
use LWP::UserAgent;
use DateTime;

use LWP::Authen::Wsse;
use Sys::Hostname;

use Filesys::DfPortable;

my $disks = {
        mogumogu => [ "/" ],
};

my $hatena_id = "HATENA_ID";
my $hatena_passwd = "PASSWORD";

use Data::Dumper;

my $ua = LWP::UserAgent->new;
$ua->credentials('graph.hatena.ne.jp:80', '', $hatena_id, $hatena_passwd);

my $hostname = hostname();

foreach my $directory ( @{$disks->{$hostname}} ) {
        my $stat = dfportable($directory);
        my $mb = ($stat->{bavail} / 1024 / 1024 / 1024 );

        #my $date = DateTime->from_epoch( epoch => time + 24 * 3600 );
        $directory =~ s|/|.|g;
        my $date = DateTime->now;
        my $res = $ua->post( 'http://graph.hatena.ne.jp/api/post', {
                graphname => (join":" , ('df', $hostname, $directory) ),
                date => $date->ymd,
                value => $mb
        });
        warn $res->content unless $res->code == 201;
}