Entry: commons markerの件数をgoogle検索結果に表示するグリモンをつくった
commons markerの件数をgoogle検索結果に表示するグリモンをつくった
[2010.01.15追記] 無駄な処理が多かったのでソースを少し整理した。
恥ずかしながら、commons markerというサービスを最近まで知らなかった。前々から出来たらいいなと思っていたことにすごく近いので使い始めてみた。
ところで、はてなブックマークのFirefoxアドオンはとても便利で、とくにGoogle検索の結果にブクマ件数が表示されるのは、クリックすべきリンクのアタリをつけるのに大変便利なのですが、同じ発想でcommons markerの件数も参考データとして意味があるかもしれないと思い、早速グリモンスクリプトで実現してみた。
googleResultWithMarker.user.jsをインストール

// ==UserScript== // @name googleResultWithMarker // @namespace http://jamadam.com/blog/ // @description googleResultWithMarker // @include http://*.google.co.jp/search* // ==/UserScript== (function() { var $; var jversion = '1.3.2'; var jexist = (typeof unsafeWindow.jQuery != 'undefined'); var conflict = (jexist && unsafeWindow.jQuery.fn.jquery != jversion); // Add jQuery if not loaded if (! jexist || conflict) { var GM_JQ = document.createElement('script'); GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/' + jversion + '/jquery.js'; GM_JQ.type = 'text/javascript'; document.getElementsByTagName('body')[0].appendChild(GM_JQ); } GM_wait(); // Check if jQuery's loaded function GM_wait() { if (typeof unsafeWindow.jQuery == 'undefined' || unsafeWindow.jQuery.fn.jquery != jversion) { window.setTimeout(GM_wait,100); } else { if (conflict) { $ = unsafeWindow.jQuery.noConflict(true); } else { $ = unsafeWindow.jQuery; } letsJQuery(); } } // All your GM code must be inside this function function letsJQuery() { var array = $('#res li.g'); var procExist = false; var i = 0; var loop = function() { if (i < array.length) { if (procExist) {return;} procExist = true; var href = array.eq(i).find('h3 a').attr("href"); var pos = array.eq(i).find('span.gl'); var url = encodeURIComponent(href); var jsurl = "http://commonsmarker.com/tools/latest_info?url=" + url; unsafeWindow["__jsonp__aD3jHjaf27mZQxt"] = function(a) { if (! a || ! a.marks || ! a.marks.length) { return; } $("<a>" + a.marks.length + " markers</a>") .attr('href', "http://commonsmarker.com/page/" + href) .css({ backgroundColor:"#f5d0d0", color:"#ff0000", fontFamily:"arial,sans-serif", fontWeight:"bold", marginLeft:"4px", padding:"1px", fontSize:"85%" }) .insertAfter(pos); } $.ajax({ type: "GET", url: jsurl, dataType: "script", complete: function() { i++; procExist = false; loop(); } }); } } loop(); } })();
commons markerの公式のインターフェースはまだ機能不足なため他のユーザーの動向がよく掴めなかったんだけど、こうしてスクリプトで簡単に件数を把握できるようにしてみて分かったことは、「全然利用されてない」ということでした。予想はしていたけど。
なお、commons markerはこの手の外部スクリプトへのインターフェースや仕様を一切公開していません。今回のスクリプトはとても強引な処理と憶測で実装しているので、サーバーの仕様が変わって動かなくなることが大いに予想されます。
Entry: ついったらータグs API(仮)
ついったらータグs API(仮)
「ついったらータグs API(仮)」は、ついったらータグs(仮)のデータベースへの問い合わせをアプリケーションから利用するためのインターフェースを提供します。ついったらータグs(仮)のデータベースにはTwitterの各ユーザーページに対する、はてなブックマークタグの情報を格納しています。本APIを利用することで、任意のユーザーに対するタグ付けの情報はもちろんのこと、任意のタグがどのユーザーに適用されているかという情報を取得することができます。また、対象となるデータベースとして、独自のルールでタグ文字列の正規化を行ったテーブルを対象にすることもできます(任意)。
全てのAPIは下記のURLで提供されます。
http://jamadam.com/th/
現在サポートされている操作は、タグ-ユーザー検索(パラメータt=api/tag.json)とユーザー-タグ検索(パラメータt=api/user.json)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできます。
タグ-ユーザー検索
任意のタグ文字列を指定することで、このタグがつけられたユーザーのスクリーンネームのリストを得ます。
パラメータ
- t (必須)
"api/tag.json"を指定します。 - str (必須)
任意のタグを指定します。 - nn (オプション)
正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。
リクエスト例
/th/?t=api/tag.json&str=it
レスポンス例
{"result":[
{"user":"takapon_jp","occurance":"2"},
{"user":"m_kumagai","occurance":"1"},
{"user":"huehara88","occurance":"1"}
]}
※実際はレスポンスに改行は含まれません。
ユーザー-タグ検索
任意のスクリーンネームを指定することで、このユーザーに付与されたタグの一覧を得ます。
パラメータ
- t (必須)
"api/user.json"を指定します。 - str (必須)
任意のスクリーンネームを指定します。 - nn (オプション)
正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。
リクエスト例
/th/?t=api/user.json&str=jamadam
レスポンス例
{"result":[
{"tag":"twitter","occurance":"38"},
{"tag":"音楽","occurance":"9"},
{"tag":"有名人","occurance":"4"},
{"tag":"webサービス","occurance":"4"}
]}
※実際はレスポンスに改行は含まれません。
実装例
ユーザー-タグ検索を利用してタグ一覧を取得するPerlコードは以下のようになります。
use LWP::UserAgent; use JSON::XS; my $tags = _twitterer_tags('jamadam'); foreach my $entry (@$tags) { print($entry->{tag}); print(":"); print($entry->{occurance}."回"); print("¥n"); } sub _twitterer_tags { my $user = shift; my $ua = LWP::UserAgent->new; my $res = $ua->get('http://jamadam.com/th/?t=api/user.json&str='. $user); my $obj = decode_json $res->content(); return $obj->{result}; }
Entry: ある意味、世界一のURL短縮サービスを作った
ある意味、世界一のURL短縮サービスを作った
[2009.09.30] Asiajinで取り上げていただきました。ありがとうございます。そして、同じネタがすでにあったと知りました。リサーチ不足。
長いURLの文字数を削減してくれるURL短縮サービスはたくさんあるけど、世界中のどの方式よりも文字数を短縮できる方法を発明しました。
サイト名ですでにネタばれですが、これは各URLに割り振るIDをマルチバイト文字の組み合わせにすることで、文字数的には短いでしょ、というネタです。ついったーなんかは完全に文字数に依存なので有効かも。
IDに使用する文字は日本の常用漢字1945字です。シーケンシャルに割り振り、使い切ったら繰り上がるので、最初の370万件は2桁に収まり、最初の73億件も3桁に収まります。
注意事項としては
- Firefox3.5, IE7, Chrome, iPhoneで正常動作を確認しました。逆に、Androidエミュレータ, DoCoMoやAUのブラウザ(フルブラウザ含む)は日本語ドメインに未対応のようです。
- IDはシーケンシャルに割り振られるので使用済みのIDは容易に推測できます。
- 「悪」などというイヤなIDが当たってしまったら
リロードして再割当してください。「縮AGAIN」ボタンを押してください。 - これはジョークサービスです。本サービスの利用に起因または関連する利用者の損害について管理者は何ら責任を負いません。
Entry: twilogで日本語ドメイン名にリンクを張るGreasemonkeyスクリプトを作った
twilogで日本語ドメイン名にリンクを張るGreasemonkeyスクリプトを作った
開発ネタがぜんぜん思いつかないのでGreasemonkeyスクリプトをもうひとつ作った。
twilogというサービスで、つぶやきの文中の日本語ドメインURLにリンクが張られていないようなので、勝手に張ってくれるGreasemonkeyスクリプトです。
// ==UserScript== // @name accessible_idn // @namespace http://jamadam.com/blog/ // @description This makes IDN URLs accessible. // @include http://twilog.org/* // ==/UserScript== (function() { var tltexts = document.getElementsByClassName('tl-text'); for (var i = 0; i < tltexts.length; i++) { var tmp = tltexts[i].innerHTML; for (var j = 0; j < tmp.length;) { if (tmp.substr(j, 3) == '<a ') { var closepos = tmp.indexOf('</a>', j); if (closepos == -1) { break; } else { j = closepos + 4 + 1; continue; } } if (tmp.substr(j, 2) == 'ht' && tmp.substr(j, 8).match(/^https?:¥/¥//)) { var end = tmp.substr(j).search(/¥s| |$/); var url = tmp.substr(j, end); var gen = '<a href="%1">%2</a>'.replace('%1', url) .replace('%2', url); tmp = tmp.substr(0, j) + gen + tmp.substr(j + end); continue; } j++; } tltexts[i].innerHTML = tmp; } })();
前回のネタよりちょっと難しかった。既設リンクをスルーする仕組みが多少いい加減。
Entry: 国際化ドメイン名をHTMLに記述する際の記述方法
国際化ドメイン名をHTMLに記述する際の記述方法
国際化ドメイン名をHTMLに記述する際の記述方法に関するメモ。間違いあったら指摘して。
(1) <a href="http://%E7%B8%AE.jp/">http://縮.jp/</a> (誤り) (2) <a href="http://縮.jp/">http://縮.jp/</a> (3) <a href="http://縮.jp/">http://縮.jp/</a> (4) <a href="http://xn--jj0a.jp/">http://縮.jp/</a>
(1)パーセントエンコード。誤り。
(2)マルチバイト文字を直接記述する。一番正しくて手軽な記述。問題点は、UAがIDNに対応していないため通信時に正しくpunycode変換されないかもしれないこと。
(3)マルチバイト文字を数値文字参照で記述する。敢えてこう記述するメリットはあまりないけど、例えばマルチバイト文字を扱えないシステムとの相互運用を想定しているためにHTML内に日本語を直接記述したくない場合には、このように記述して構わない。
(4)punycode変換して記述する。リンクを正しく動作させるという点では一番安全かもしれない記述。ただし、エンドユーザーが知りうるURLが全てデコードされるかどうかはブラウザの実装次第。例えば「リンクをコピー」したときにpunycodeのまま出力されてしまってはIDNの利点が台無し。punycode変換はUAが通信時に行うことが推奨されているため、HTMLに記述する時点で行う必要はない。
Entry: 縮.jpに待望の「縮AGAIN」ボタンを搭載
縮.jpに待望の「縮AGAIN」ボタンを搭載
縮.jp。7月に作って放置してたら10月になって話題になった。人生で一番集客した。そんな訳で、ネタを必要以上に膨らませるべく、システムを改修しました。見た目的にはあまり変化ありませんが、中身はほぼ丸々変わってます。特筆すべき変化は。。
待望の「縮AGAIN」ボタンを搭載
生成された短縮URLが気に入らなかった場合、このボタンを押すと別のURLを生成してくれます。なお、縮AGAINした場合、古いURLはしばらくして解放されます。
既設の短縮URLがあれば、短い順にご提案
今までは湯水のように新規作成してましたが、縮ボタンを押した際、まずは既設の短縮URLを提示するようになりました。縮AGEINすると短い順に既設URLが表示され、なくなれば新規作成されます。
転送時、末尾のゴミを可能な限り取り除く
Twitterでhttp://縮.jp/上これすげーみたいなつぶやきが多発していたので、こういう場合はhttp://縮.jp/上を検知して転送します。Perlでいうと、
$id =~ s/¥P{Han}.*//;
となっています。漢字を表す正規表現なんて初めて知りました。
ちなみに、ファイルベースからPostgresqlに移行
パフォーマンスいいかなと思って、1件1ファイルなどというデータ管理をしてたんですが、IDがシーケンシャルに固定されるとか、逆引きできないとか、色々問題あったのでPostgresqlにしました。なお、ファイルベースのキャッシュの仕組みを導入したので転送時のパフォーマンスは以前と変わらない。はず。
APIにも変更あり
offsetというパラメータが新設されました。これは前述の既設URLの再利用と関連するもので、任意の既設URLを取り出すためのパラメータです。offsetを十分に大きくすると自動的に新規作成されます。通常は指定しないでください。
既知の問題
- 元URLがbit.ly等ですでに短縮済みだった場合、iPhoneで転送されない。確かソフトバンクの仕様でiPhoneで多段リダイレクトできない。
- Twitter用のいくつかのGreaseMonkeyスクリプトやアドオンで縮.jpへ飛べない。FirefoxのJavascriptのバグっぽい挙動が原因と思われる。対処法はこちら
- Twitter周辺サービスで日本語ドメインが誤ってパーセントエンコードされているケースも見かけます。サーバサイドでの処理に問題があるのではないか(憶測)。
- iPhoneの多くのアプリで開けない。おそらくアプリからwebkitだかのAPI的なものに渡すURLをpunycode変換してないのではないか(憶測)。対処法はこちらの12月15日の記事くらいしか見当たらなかった。
今後の予定
任意のIDを指定可能に- 不人気文字を避ける仕組み
- 元URLがすでに短縮URLだった場合に展開してから短縮
- スパム対策
Entry: 縮 REST API
縮 REST API
「縮 REST API」は、URL短縮/転送サービス「縮.jp」の機能を、アプリケーションから利用するためのインターフェースを提供します。
全てのAPIは下記のURL以下で提供されます。
http://api.xn--jj0a.jp/
現在サポートされている操作は、短縮URLの生成(generate.jsonへのPOST)と短縮URLの展開(lookup.jsonへのGET)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできるかもしれません。
GENERATE
任意のURLを渡すことで、新規に短縮URLを生成します。
パラメータ
- fUrl(必須)
短縮対象のURL。http://かhttps://で始まる必要があります。 - customId(オプション)
任意の漢字の組み合わせで短縮URLの識別子を指定します。utf8でパーセントエンコードしてください。 - offset(オプション)
fUrlで指定したURLに対する既設の短縮URLがひとつ以上存在する場合、このパラメータに0以上の数値を指定することで任意の候補を選択することができます。既設URLは文字数の短い順に並んでいます。offsetが既設URLの数より大きいとき、新しいURLが生成されます。このパラメータは特殊な目的がない場合は指定しないでください。
リクエスト例
POST /generate.json
fUrl=http://example.com&id=%E4%B8%83
レスポンス例
{"result":{"fUrl":"http://example.com/","generated":"http://縮.jp/七"}}
LOOKUP
既存の短縮URLの識別子を受け取り、元のURLを返します。識別子はUTF-8でパーセントエンコードしてください。
パラメータ
- id(必須)
URLの識別子を指定します。 - hash
後方互換性のための、idの別名です。(廃止の予定)
リクエスト
GET /lookup.json?id=%E4%B8%83
レスポンス
{"result":{"fUrl":"http://example.com"}}
共通
HTTPレスポンスコード
正常/エラーに関わらず「200 OK」を返します。
エラー時のレスポンスボディ
下記の書式でJSONデータを返します。
{"error":"[エラーコード] [説明]"}
エラーコード一覧
- 400: generateの際、fUrlの書式が正しくない、customIdに不正な文字が含まれる、など。
- 409: generateの際、customIdがすでに使用されている、など。
- 404: lookupの際、指定のidが存在しない、など。
実装例
generate.jsonを利用して短縮URLを取得するPerlコードは以下のようになります。
use LWP::UserAgent; use JSON::XS; my $generated = _shuku('http://example.com'); sub _shuku { my $ua = LWP::UserAgent->new; my $res = $ua->post('http://api.xn--jj0a.jp/generate.json', {'fUrl' => $_[0]}); my $obj = decode_json $res->content(); my $decoded = $obj->{result}->{generated}; utf8::decode($decoded); return $decoded; }
Entry: 縮.jpがカスタムID機能を搭載!
Entry: 縮.jpのバグを直した
縮.jpのバグを直した
先日、縮.jpを国際化ドメイン対応にした際にパス名がぶった切られてたのに気づいたので直した。そして、せっかくなので国際化ドメインへの対応方法について、ちらしの裏的メモ。
Locationヘッダにutf8のバイト列を流し込んでもうまく行かなかったのでpunycodeに変換した。
use Net::IDN::Punycode; my $url = 'http://例示.日本/path/'; my $fixed_url = ''; my ($schema, $domain, $uri) = ($url =~ m!(https?)://([^/]+)(.+)!); foreach my $part (split(/¥./, $domain)) { if ($part =~ /¥P{IsASCII}/) { $fixed_url .= 'xn--'. encode_punycode($part). '.'; } else { $fixed_url .= $part. '.'; } } chop($fixed_url); printf("Location: %s://%s¥n¥n", $schema, $fixed_url. $uri);
cpanでいくつかモジュールが見つかるけど、Net::IDN::Punycodeが正解。IDNA::Punycodeは古いから利用率高いけどバグもの。
上記の結果は
http://xn--fsq192h.xn--wgv71a/path/
ちなみに国際化ドメインと国際化URI(って呼んでいいのか?)への対処方法は全く別で、さらに国際化URIは国際化ファイル名と国際化クエリーに対処しないとならない気がするので、その辺を今度整理してみよう。
Entry: ブックマーク
ブックマーク
- Slide box - a jQuery Plug-in
- Twitterを楽しむ10の方法 (my ver)
- Command Center
- NTTドコモからiPhone 3Gへの絵文字送信が可能に--全キャリア間に対応
- 2万以上の“一目ぼれ”記録「ヒトメボ」2万ユーザー突破
一目ぼれのうち、0.6%が両思いという結果が出ている。
少ねー。しかも三角関係だったらいやだな。 - バリデーションルールをインラインでわかりやすく記述できるjQueryのプラグイ『validationEngine.js』
あらら、classにデータを書いちゃう? AJAX URLも埋め込んでもいいの?
Subscribe to my RSS feed