やしお

ふつうの会社員の日記です。

自分のはてなダイアリー内の記事をランダムリンク by Google Gadget

 この世界の誰かに向けての参考、または私の備忘録です。

【概要】

 自分のはてなダイアリー内の記事にランダムで飛べるボタンを備えたGoogleガジェットの作り方。また、そのサイドバーへの設置まで。
はてなダイアリーでは今のところJavaScriptを直接使用できないため、Googleガジェットを利用しました。

【サンプル】

【導入手順】

 本当はスクリーンショットを入れたほうがわかりやすいって知ってるけど、知ってるけど、えへへ、やらないよー。


(1) Googleガジェット自作のための環境づくり
Googleのアカウントをとります。iGoogleを使えるようにします。


iGoogleに「Google Gadget Editor」と「My Gadgets」というガジェットを追加します。
 下のボタンをクリックするとiGoogleに追加されます。


Google Gadget Editor
Add to iGoogle


My Gadgets
Add to iGoogle


(2) ソースの手直し
 下の方にソースを記載します。それをテキストエディタか何かで必要なところを変えてください。


(3) ガジェットとしてiGoogleへ追加
Google Gadget Editorの編集欄へソースをコピペ
Google Gadget Editorのメニューから適当な名前でSAVE
Google Gadget Editorの右端らへんにあるxmlへのリンクを右クリックしてURLをコピー
・My Gadgets一番下のテキストボックス「Add a gadget」へURLをペースト。「Add」ボタンをクリック
(第三者がどうのこうのみたいな警告が出てくるけど気にせずOK)
・これでiGoogleへガジェットが登録されます


(4) 動作チェック
 iGoogleに登録したガジェットのボタンを押してみて、自分のはてなダイアリー内の記事へランダムに飛べるか確認してみてください。
Google Gadget Editorは動作チェックも本来できるのですが、makeRequestらへん(?)の動作をしてくれないみたいなので、ちょっと面倒くさいのですが一旦iGoogleに入れて動作させてみないとダメみたいです。


(5) ガジェットのコードを生成
・ガジェットのバーにある「▽」ボタンからメニューを出して「このガジェットについて」をクリック
・表示されたページ右の「ウェブマスター向け」にある「このガジェットを埋め込む »」をクリック
・適当にサイズを変更したり枠線を選んだりした後、「コードを取得」のボタンをクリック
・コードが表示されるのでこれをコピー


(6) はてなダイアリーへ貼り付け
 たぶんこういうのをサイドバーにでも入れれば、たぶんOK。

<div class="hatena-module">
  <div class="hatena-moduletitle">ランダムリンク</div>
  <div class="hatena-modulebody">
(さっきコピーしたscriptタグのをココにコピー)
  </div>
</div>

【ソース】

<?xml version="1.0" encoding="UTF-8" ?>
<Module>  
  <ModulePrefs title="ランダムリンク">
  </ModulePrefs>
  <Content type="html">
    <![CDATA[

<script language="JavaScript"><!--

basicUrl = "http://d.hatena.ne.jp/Yashio/";
startDate = new Date(2007, 3, 23);
endDate = new Date();  // 引数無しで「現在日時」

pageExistStr = "<h3>";  // 記事の有無を判定する文字列
maxCnt = 20;  // 最大試行回数
waitComment = "もうすぐ飛ぶよ";

pageExistFlg = 0;
cnt = 0;
generateUrl = '';


function getHtml(url){
  var params = {};
  params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
  gadgets.io.makeRequest(url, response, params);
}

function response(obj){
  pageExistFlg = obj.text.indexOf(pageExistStr);

  waitComment += '.';
  document.getElementById('content_div').innerHTML = waitComment;

  if(pageExistFlg <= 0 && cnt < maxCnt){
    cnt++;
    generateUrl = basicUrl + datenum2str(makeRndDate());
    getHtml(generateUrl);
  }else{
    top.location.href = generateUrl;
  }
}

function makeRndDate(){
  var dateRange = endDate.getTime() - startDate.getTime();
  return Math.floor(Math.random() * dateRange) + startDate.getTime();
}

function datenum2str(rndDateV){
  var rndDate = new Date();
  rndDate.setTime(rndDateV);

  var yy = rndDate.getYear();
  var mm = rndDate.getMonth() + 1;
  var dd = rndDate.getDate();
  if(yy < 2000){ yy += 1900; }
  if(mm < 10){ mm = "0" + mm; }
  if(dd < 10){ dd = "0" + dd; }

  return '' + yy + mm + dd;  //「''」を入れないと数値で足される場合があるみたい
}


function randomLink(){
  cnt = 0;
  teststring = '';

  generateUrl = basicUrl + datenum2str(makeRndDate());
  getHtml(generateUrl);
}


// --></script>

<center><form>
<input type="button" value="どこかへ" onclick="randomLink()">
</form></center>
<div id="content_div" style="font-size:11px;"></div>

    ]]>
  </Content>
</Module>

 ユーザごとに変更する必要が生じるのは下記の点です。

basicUrl = "http://d.hatena.ne.jp/Yashio/";
  はてなダイアリーのURLです。自分のidに変える必要があります。

startDate = new Date(2007, 3, 23);
  ランダムでアクセスさせたい範囲の、始まりの日付です。
  一番最初の記事の日付を入れるといいかと思います。

endDate = new Date();
  ランダムでアクセスさせたい範囲の、終わりの日付です。
  引数無しで「今日」になります。
  引数を(startDateみたいに)指定すれば終わりの日になります。

【処理の内容】

(0) ボタンが押されると、
(1) ランダムで8桁の日付列を生成(例:20100906)
(2) basicUrl(例:http://d.hatena.ne.jp/Yashio/)の後ろにくっつける(例:http://d.hatena.ne.jp/Yashio/20100906/
(3) そのURL(generateUrl)に記事が存在するか判定する
(4) 記事が存在するURLを得られたか、繰り返し回数がmaxCntを超過するまで(1)-(3)を繰り返し実行
(5) そのURL(generateUrl)に飛ぶ

【恥さらしと、言い訳】

・処理が遅い
 見てのとおり非常に効率の悪いことをしています。
 生成したURL(generateUrl)が記事を持っているかどうかを判定するのにわざわざそのページを読み込んでいます。はてなダイアリーでは記事のない日付のURLでもちゃんと記事のないページを表示してくれるので、いったんページ自体を読み込んで文字列pageExistStr(今回はh3タグにしてみました)が存在するかどうかを確認しないと記事があるかどうかわからないかなと思って。
 しかもこの作業を最大でmaxCnt回(今回は20回)、見つかるまで繰り返す……ひー!
 registerOnLoadHandlerでユーザー(?)がボタンを押すまでの間にこっそり済ませておこうかとも考えたのですが、押される機会も少なそうなこのボタンのために裏で読み込んでるというのもどうなのかなあ、と思ってやめました。


・ランダムに日付列を生成する→なかなか記事の存在する日に当たらない場合がある
 特に(ランダム生成範囲に対して)更新頻度が低いダイアリーの場合だと外れる確率が高くなります。最悪、試行回数maxCntのうちに見つけられずに記事のないページに飛ぶことがあります。
 最初、archiveのページを取得して、記事の存在する年月を当て、次にその年月のarchiveを取得して記事の存在する日を当てる、という2段構えでやれば、外れる確率も小さいし何より何度もページを読み込む必要がない(2回で済む)な、と考えたのですがそれだと更新数の多い月の記事が当たる確率<少ない月の記事が当たる確率になってしまうなと思ってやめました。
 あとendDateに今日の日付を入れている場合で、かつずーっと放置されているようなダイアリーだとまるで飛べなくなるかもしれません。はてなダイアリーのトップページにアクセスして最新記事の日付をとってこようかと思いましたが、単純に面倒くさいのでやめました。
 記事のある日付リスト、みたいのがどこかにポンと置いてあれば、何もかも解決するんですが……ないのかしら……


・ボタン連打
 ボタンを連打されると記事のないところへ飛んだりします。きちんと検討したわけではないけど、グローバル関数のgenerateURLやcntがぶつかってるんだと思います。
 時間がかかる件もあわせて、ボタンを押した後、下に「もうすぐ飛ぶよ....」(処理が進むたびにコンマが増える)表示を出して「ちゃんと動いてるよ大丈夫だよ」と安心してもらってボタン連打を抑止しようという目論みというか足掻きをしてみた。排他的に処理するように直せばいいのかもしれないけど……もう面倒臭くて……


再帰
 当初、すべての処理が直列に実行されるものと思い込んで普通(?)にgetHtmlをfor文の中で回していたのですが、getHtmlは(というかmakeRequestは)自身の処理の完了を待たずにすみやかに次の処理へ渡るので一瞬でmaxCnt個分のURLが生成された挙句、最後のURLでジャンプするということになっていました。何も理解していない証拠です。
 結局、コールバック関数(response)の中でgetHtmlを再度呼び出しています。コールバック関数はmakeRequestのフェッチが終わってから呼び出されるので、この中に書いておけば勝手に先に処理されたりしない……当たり前ですが。それにしても再帰呼び出しみたいなことをしてもいいものなのかしら。内部的なことをきちんと理解していないのでよくわかっていないところがあります。順番に親の関数は消えていってくれそうに思えるんだけど、本当に大丈夫なのかしら。うーん。


・汎用性?
 今いるはてなダイアリーのアドレスを自分で取得したりとか、0000年00月00日みたいのにも飛べるようにするだとか、誰でも置くだけで使えるようにするとか、いろいろ考えてはみたけれど、考えてみただけです。


【経緯】

 さらに混沌としてきます。


 のりしろさん(id:norishiro7)はかく語りき。

このブログ内の記事にランダムで飛ぶリンクのスプリクトだかプログラムだかを作ってもらいたいのです
id:norishiro7:20100901

→ランダムリンクとかどうやるのかしら
→なんかJavaScriptでできるらしい(JavaScript書いたことない……)
はてなではJavaScriptは使えないらしい
Googleガジェットがはてなで使えるようになったから実質javascriptが解禁されたということらしい(え、Googleガジェットって何……?)
Googleガジェットを自作する方法を調べる(え、iGoogleって何……?)
Googleガジェットはどこか別の場所に置かれたxmlファイルを読み込んで動く
→「どこか別の場所」って用意しなきゃいけないの? めんどい……
→いろんな解説サイトではGoogle Page Createrに置くのが簡単じゃないかという
Google Page Createrはもうサービス終了らしい。Google Sitesというサービスに移行したとか
Google Sitesに登録。サンプルプログラムを書いたxmlファイルをアップロード
→なんかうまくいかない。「My Gadgets」(Essential tool for gadget developersだって)にxmlファイルのURLを入れて登録しようとしても「ねーよ」と言われるし、何とかiGoogle上に自作ガジェットを登録できても、xmlファイルを更新してもガジェットが更新されなかったりする(Google Sitesでファイルのバージョン管理をありがたくもしてくれるおかげか、更新前の状態で表示されたりする)イライラー!
Google Gadget Editor(Googleが提供しているガジェット)で解決。編集もできてGoogleのサーバーにxmlファイルを保存しといてくれるので、これをそのまま利用すれば自作ガジェットが。Google Sitesに振り回された私の時間って……
→ここから、おプログラミング(?)の時間
→Cしか触ったことがないので型を明示しないのに戸惑ったり、クラス/オブジェクトをぜんぜん理解していなかったり……
→とまれかくまれ、"http://d.hatena.ne.jp/Yashio/"の後ろに8桁の日付をランダムで付加(ただしブログ開始日〜今日までの日付)したアドレスに、ボタンを押すと飛べる、というガジェットが完成(これ自体はチョー簡単)
→でもランダムなので記事のない日にも飛ぶ。ダサい
→なんとか生成したURLの先に記事があるかどうか、あらかじめ知りたい
JavaScriptは(AJAXは?)別ドメインのファイルを取得できないらしい。え……?
→「Gadgets API を使用すると、他のウェブ サーバーやウェブページからリモート コンテンツをフェッチし、処理できます」(http://code.google.com/intl/ja/apis/gadgets/docs/remote-content.html)だって。げへげへ。
→(いろいろあほなことをした末)なんとかできた。ボタンを押してから時間がかかるので「もうすぐ飛ぶよ...」のメッセージをつけてみた。
→のりしろさんに無事納入。

【参考】

はてなダイアリーJavaScript eval - てっく煮ブログ
http://d.hatena.ne.jp/nitoyon/20070820/javascript_eval_on_hatena_diary
はてなダイアリーJavaScriptを使う方法を知ったのがここでした。


Googleガジェットの作り方 - Google Gadgetのページ
http://wiki.livedoor.jp/hogematomo/d/Google%a5%ac%a5%b8%a5%a7%a5%c3%a5%c8%a4%ce%ba%ee%a4%ea%ca%fd
ところどころ古い情報であたしのこころを弄んだけど、わかりやすくてありがたいねえ


・はじめてのiGoogleガジェット開発
http://zapanet.info/blog/item/1136
そもそもGoogleガジェットもiGoogle自体も知らなかったのです


・リモート コンテンツの処理 - Gadgets API - Google Code
http://code.google.com/intl/ja/apis/gadgets/docs/remote-content.html
google本家の解説。用語がいちいちわからないので自分の無知を呪う。


・[入門] 外部URLを取得する (makeRequest()) (OpenSocial大技林(予))
http://katsubemakito.net/opensocial/bgn_makerequest1.html
makeRequest関数使い方サンプル。ああ、実使用例はなんてわかりやすいんだ!


・とほほのJavaScriptリファレンス
http://www.tohoho-web.com/js/index.htm
そもそもJavaScriptを触るのがはじめてだったので。

【最後に】

 こういう妙に長い記事にひっかかると、このランダムリンクで飛ぶのに時間がかかるんですね。