トップページ

2008年04月03日

Atom 1.0 リファレンス

RFC4287の原文と日本語翻訳+構造図で、インタラクティブに参照するAtom(Syndication Format)の仕様。

» Atom 1.0 リファレンス

Atom 1.0 リファレンス

以下は余談。

最初はRSS 0.9x/2.0とRSS 1.0とAtomとの簡単な比較表を作ろうと思ってたのでした(そして、RSS 1.0やAtomの仕様は、杜撰な鷹揚なRSS 0.9x/2.0のそれと比べて厳格なので、各フォーマットを共通のデータソースから生成しようとする場合には、データソースの準備は厳格な側に合わせてやらないとワヤになりまっせ、という講釈を垂れようとしていた)。

で、しかしRFC4287を眺めてたら、Relax NG(のコンパクト構文)スキーマによるストラクチャー表現を、ブロック図に置き換えて操作できるようにしたら面白いかもと思い立ち(この時点で比較表を作ろうという当初の目的は潰える)。しかし要素を並べただけではアレなので説明を加えるにしても、RFC原文のコピペだけでは不親切かなということで、ついでに日本語訳も用意してみた次第。

既存のRFC4287の日本語翻訳

» 「Atom 1.0 リファレンス」の続きを読む

Category: ウェブ制作
Posted 2008年04月03日 00:49

2008年04月02日

iMenuのGoogle検索がいきなりおかしなことになってる件

Googleモバイルで「プロフィール」や「プロフ」を検索すれば、1位は前略プロフだろうと思うのは素人。

http://www.google.co.jp/imode/search?ie=Shift_JIS&mrestrict=chtml&site=mobile&q=%83v%83%8D%83t%83B%81%5B%83%8B

確かに、PCで普通に検索してみれば、少なくとも本日ただ今現在の1位は前略プロフで、そのPCで、Googleモバイルで「プロフィール」を検索する上のアドレスを開けば、本日ただ今現在の結果は以下のような感じで、やはり前略プロフが1位。

  1. 前略プロフィール
    『前略プロフィール』は、誰でも手軽に携帯電話・パソコンの両方に対応した、自己 ...
    pr.cgiboy.com/
  2. FC2プロフ - あなた専用のプロフィールを作ってみよう!!
    あなた専用のプロフィールが簡単に作れちゃう!!プロフィールを作ってみよう!!
    pr.fc2.com/
  3. Amazon.co.jp: プロフィール: デューク・ピアソン,ジーン・テイラー ...
    Amazon.co.jp: プロフィール: デューク・ピアソン,ジーン・テイラー, ...
    www.amazon.co.jp/プロフィール-デューク・ピアソン/d...
  4. 佐野夏芽プロフィール
    タレントプロフィール ・ フォトダイアリー ・ タレント募集 ・ メールマガジン登録 ...
    www.metalbox.co.jp/work/sano/pcolum...
  5. みんなのプロフィール
    みんなのプロフィール 名前をクリックするとプロフィールを閲覧できます。 ...
    www.yukinosake.com/cgi-bin/profile/...

しかし、少なくとも本日ただ今現在、「PREF」という名称のcookieを削除してから上記のモバイル版のアドレスを開いてみると、以下のようにまったく違う結果が表示される。1位は関西のデリヘル。ちなみにこのcookieがない状態で100位(10ページ)まで検索しても、前略プロフは出てこない(以下にもあるような、前略を騙るサイトは出てくる)。

  1. 大阪・兵庫・京都・奈良・和歌山巨乳ロリ・ギャル系専門デリバリーヘルス ...
    プロフィール南大阪 ・ プロフィール京都 ・ プロフィール兵庫 ・ プロフィール奈良 ・ プロフィール和歌山 ...
    www.pro-file.jp/m/
  2. 携帯(ケータイ)無料プロフ作成フリーサイト「My・ぷろふぃーる」
    完全無料でマイプロフィール交換画面を作成できる携帯電話専用プロフ提供フリー ...
    vl-o-l.jp/
  3. プロフィール
    プロフィール. 名前:すぬーぴー. 種別:ビーグル犬の. ぬいぐるみ. 年齢:不明 ...
    penguinmaru.fc2web.com/i/profile.html
  4. 前略プロフィール by aimew
    プロフガイドは数あるプロフィールサイトを厳選してご紹介!もちろんプロフを通じて新しい ...
    aimew.jp/zennryaku/
  5. 前略プロフィール by aimew
    JC、JKに人気のあるプロフィールサイト♪. 前略出会い系 ...
    aimew.jp/zennryakud/
  6. 携帯無料プロフ作成 チェキ!プロフィール
    チェキ!プロフィールでは簡単☆無料でプロフィールサイトを作成できます!
    chpr.jp/
  7. プッチギャル
    女の子コメント:いちゃx2楽しい時間を過ごしましょうねぇ(*^^)v ...
    www.kobe-pucchigal.com/m/gal/k-view...
  8. 京都デリヘル/プッチギャル
    キャッチコピー:☆京都に渋谷系GAL誕生☆ドエロEカップに大興奮! ...
    www.kyoto-pucchigal.com/m/gal/k-vie...

さて、cookieの有無で検索結果が変わるということは、cookieを喰わないドコモの端末ではどうなるかというと、もちろん上と同じ、1位がデリヘルという結果になる。

で、ドコモは4月1日からiMenuのトップ画面にGoogleと連携した検索機能を追加し、Googleの検索エンジンにより、携帯電話向け一般サイト、PCサイトの検索結果も表示されるようになったのだけど、そのiMenuの検索フォームから「プロフィール」を検索してみた結果が以下。

  1. 携帯(ケータイ)無料プロフ作成フリーサイト「My・ぷろふぃーる」
    完全無料でマイプロフィール交換画面を作成できる携帯電話専用プロフ提供フリー ...
    vl-o-l.jp/
  2. プロフィール
    プロフィール. 名前:すぬーぴー. 種別:ビーグル犬の. ぬいぐるみ. 年齢:不明 ...
    penguinmaru.fc2web.com/i/profile.html
  3. 前略プロフィール by aimew
    JC、JKに人気のあるプロフィールサイト♪. 前略出会い系 ...
    aimew.jp/zennryakud/
  4. 京都デリヘル/プッチギャル
    キャッチコピー:☆京都に渋谷系GAL誕生☆ドエロEカップに大興奮! ...
    www.kyoto-pucchigal.com/m/gal/k-vie...

一瞥して明らかなように、これは上の、通常のGoogleモバイルでの結果のサブセット。1位のデリヘルが消えているのは、おそらくドコモなりのフィルターで、「有害」と判定されているサイトが除去されてるためではないかと思われ。

しかし京都のデリヘルは見事このフィルターをくぐり抜け4位に躍進!(4位だとiMenu検索の1ページ目に表示されるので、それ以下とは雲泥の差)なんという脅威のSEO!(違)

………とまあ、そんなこんなで。これはたまたま出くわした極端な例ですが、別にこの例に限らず、ドコモ端末でGoogleモバイルを利用すると、検索結果がなんだかおかしなことになってる模様です、というお話ですた。

Category: ウェブ制作
Posted 2008年04月02日 23:41

2007年06月01日

opm.blitzed.org is pointing at a blackholed nameserver

MovableTypeの管理画面にあるSixApartからの告知に、【重要】コメント投稿に時間がかかる現象についてと出ていて、なにかと思ったら、opm.blitzed.orgが停止したので、バンドルされているSpamLookupプラグインの、DNSBLサーバの設定がデフォルトのままだとヤバイですよ、というハナシで。

いや、ちょっと待てと。OPMが停止したのは一年も前なわけで。それを今頃になって告知かと。OPM statusによると、今年の5月にopm.blitzed.org is pointing at a blackholed nameserverということになったそうで、そのため告知にあるような事態が生じるようになって問題が顕在化したらしい。

で、なんでブラックホールリストのサーバがブラックホール行きになったかというと、in order to reduce query load on our serversだそうで。なんで未だにそんな状態かというと、多くのMTユーザが(もちろんMTだけじゃないだろうけど)OPMの停止を知らずに、デフォルトで設定されているサーバをそのまま使い続けていたであろうからで。なんでそんなことになってるかというと、誰かに言われない限り、普通はDNSBLサーバが停まってるなんてことには気付かないよな、と(ましてデフォルトで設定されてるものだし)。

多くのDNSBLサービスはボランティアな運営なわけで、いつなくなっても不思議ではなく。そもそもDNSBLは、ボットネットの拡大によって以前ほど効果的ではなくなっており、ORDB.orgのようにこれを理由に閉鎖してしまった例もあり。あるいはzen.spamhaus.orgのように、新たなサーバが用意され以前のもののリプレイスが推奨される場合もあり。斯様な状況でDNSBLを利用する場合は、利用するサーバの状況を定期的に確認して設定を見直す必要があるわけですが、それをすべてのユーザにやれというのも無茶なハナシで。しかしMTには折角管理画面に告知コーナーがあるのだから、早期に告知がされていればユーザもSixApartも、そしてBlitzed.orgも無用のトラブルを抱えずに済んだのになあ、と。

………と、OPMの死亡告知を死亡から半年後に出したヤツが、自分のことは棚にあげて自戒の念を込めて申し上げてみるテストでございました。合掌。

以下はMTのハナシではないけれど、DNSBLネタが少々。

Category: ウェブ制作
Posted 2007年06月01日 23:59

Firefox 2/IE7用、OpenSearch検索プラグイン(検索プロバイダ)作成ツール

検索バー

Firefox 2やInternet Explorer 7などの検索バーに検索サイトを追加するOpenSearch形式のプラグインを、極力簡単に作れるようにしてみました、というものです。要Cookie+JavaScript。実は随分前に作って、しかし腑に落ちないまま放置していたものを引っ張り出してきて、全面改装。

» OpenSearch検索プラグイン(検索プロバイダ)作成ツール

ものは試し。上のリンク先で、ページの右側の「ブックマークレット」欄にある、「検索プラグインの作成」というリンクを、マウスの右クリックとかでブックマーク(お気に入り)やツールバーに登録してみてくださいな。

で、例えばGoogleとかで、「TEST」を検索した結果のページでそのブックマークレットを選択してみてください。下図のような表示が出てきたら正解。以上の操作がメンドクセーひとはここをクリック(笑)。

OpenSearch検索プラグイン(検索プロバイダ)作成ツール

「検索プラグインを追加」ボタンをクリックすると、確認ダイアログが表示され、プラグインを追加できるわけですが、お気に召さない項目があれば適宜編集していただければと存じます。各項目の説明は、その入力欄をクリックすると説明が出てくる筈。編集後には「確認・更新」ボタンをクリックしてください。

「名前」は、ウェブブラウザはこれでプラグインを管理してるので、既に登録済みのプラグインと同じ名前は付けられません。

「アイコン」は、Firefox 2の検索バーに表示されるものです(IE7では利用されません)。アイコンはウェブサイトのFaviconを自動検出しますが、Faviconがないサイトや、自分好みのアイコンを使いたい場合は、画像をアップロードすることもできます。画像がJPEGやGIF、PNGなら、自動的に画像の中央部を切り出してアイコンサイズに縮小するので、あらかじめアイコンサイズの画像を用意しておく必要もありません。

なお、ここで検索プラグインを作成できるのは、GETメソッドの検索フォームのみです。つまり、検索結果のページのアドレスに、(検索キーワードである)「TEST」という文字が含まれないものは作成できません(POSTメソッドによる検索プラグインは、OpenSearchの拡張仕様のひとつとして用意されているParameter要素を使って作成できますが、IE7はこれに対応していないためPOSTメソッドのプラグインは利用できません)。

いまでは多くのウェブサイトがオフィシャルな検索プラグインを提供していますが、提供されてない(提供しそうにもない)サイトや、ありそうでなさそうなサイトや、あるいは提供されてるけど名前やアイコンが気に入らない類は、自分で(勝手に)作っちゃえばいんじゃないかと。以下は、現時点でオフィシャルが存在しないものをテキトーに見繕ってみた例。

» 「Firefox 2/IE7用、OpenSearch検索プラグイン(検索プロバイダ)作成ツール」の続きを読む

Category: ウェブ制作
Posted 2007年06月01日 19:22

2007年05月26日

HTMLをXML化してDOMやXPathで操作するWebスクレイピング用PHPクラス

SimpleXMLHTMLParser or TidyHTTP_RequestCache_Liteの組み合わせで、リモートサイトから取得したHTMLソースを整形して、SimpleXMLオブジェクトに変換するライブラリでございます。サーバサイドはもとよりクライアントサイドのAjaxでも、DOMやXPathなどを用いたオブジェクト操作によるScrapingが可能になります。要SimpleXMLなのでPHP5専用(もっとも、SimpleXMLをDOM XMLに置き換えればPHP4でも同じようなことはできますが)。

ええっと手っ取り早く、以下にアクセスしてフォームに適当なURLを入力してみてください。

下図のようにURLがダラダラとリストされれば正解。

test_ajax.htmlの実行例

ここでリストされるURLは、ご覧いただければおわかりの通り、入力したURLのウェブページ内にあるA要素のHREF属性値なわけですが、ポイントはこれを、responseXMLからDOMで取得していること。つまりXMLHttpRequestのリクエスト先は、well-formedなXMLを返してるということでございます。あと、HREF属性値はすべてフルパスに変換されてたりもします。

そのXMLHttpRequestのリクエスト先であるPHPアプリのソースは以下の通り。

<?php

if (!isset($_GET['url']) or empty($_GET['url'])) {
    header("$_SERVER[SERVER_PROTOCOL] 400 Bad Request");
    header('Content-Type: text/plain;charset=UTF-8');
    exit('The URL is not specified.');
} else {
    require_once 'HTMLScraping.class.php';
    $s = new HTMLScraping;
    try {
        $xml = $s->getXmlObject($_GET['url']);
    } catch (Exception $e) {
        header("$_SERVER[SERVER_PROTOCOL] 400 Bad Request");
        header('Content-Type: text/plain;charset=UTF-8');
        exit($e->getMessage());
    }
    $s->convertPath($xml, array('a' => 'href'));
    header('Content-Type: application/xml;charset=UTF-8');
    exit($xml->asXML());
}

?>

HTMLScraping->getXmlObject()は、第1パラメータのURLで取得したHTMLソースから生成したSimpleXMLオブジェクトを返します(エラーの際には例外を投げますんで、上の例のようにtry~catchしてやってください)。

ちなみに、キャッシュ処理を行う場合、まずは以下のように、コンストラクタの第1パラメータにキャッシュファイルを保存するディレクトリを指定しておきます(このディレクトリはもちろん、PHPアプリに対するread/writeパーミッションが必要です)。

$s = new HTMLScraping('/tmp/');

で、getXmlObject()の第2パラメータに秒数を指定すると、URLのレスポンスが指定した秒数キャッシュされます。

$xml = $s->getXmlObject($_GET['url'], 3600);

その他のオプションパラメータやその他のメソッドの説明、及び配布アーカイブのダウンロードなどは、説明ページへどーぞ。

応用例:HTMLをScrapingしてRSSフィードを生成する

さてこれだけでは面白くないので、これの応用例としてHTMLからRSSフィードを生成するということをやってみました。と言ってもHTMLの内容は多様で一律に処理できるわけはないので、共通処理をまとめたabstractなクラスを用意し、この継承クラスでオーバーライドするメソッド内で、HTMLScraping->getXmlObject()で取得したSimpleXMLオブジェクトから必要なデータを抽出していただこうという寸法でございます。

注意:これはあくまで応用例として用意したもので、実用に供されることを意図したものではありません。

配布アーカイブ中のexamplesディレクトリにあるtest_feed.phpはこのサンプルアプリケーションで、同ディレクトリ内にあるtest_feed.htmlからリストアイテム要素を取り出し、フィードを構築して出力します(このアプリケーションはキャッシュ機能を使用しますので、実際に自分の環境で実行する場合は、コンストラクタのパラメータで指定しているキャッシュファイルの保存先ディレクトリ名を、実際に利用可能なものに変更してください)。

HTMLToFeed.class.phpは、test_feed.php内で継承しているabstractクラスHTMLToFeedを定義しています。HTMLToFeed->getFeed()は、DOMで構築したフィードを出力しますが、キャッシュ処理が有効な場合は、フィードの出力をキャッシュし、出力にLast-Modified/Etagヘッダを含めて、If-Modified-Since/If-None-Matchによる条件付きリクエストに応答します。

test_feed.phpでは、アクセス先のURLが単一(しかもコンテンツが変更されない)であるため、出力のみをキャッシュしていますが、複数のURLにアクセスしてその内容をマージするような場合、URLごとに、その更新頻度に応じてキャッシュの有効期間を設定し、それに加えて出力もキャッシュすることで、アプリケーションのパフォーマンスを向上させることができるかもしれません。

DisclaimrBETA

まあこれは、こんな感じでこんなことができますよというサンプルであって、これを広く使ってもらおうなんていう気は毛頭なく。仕様も自分の好みでやらかしてるので、あんまし汎用的ではないかもしれず。そもそもライブラリの組み合わせからして、別段この組み合わせに限ったわけでも、もちろんありません。

特に、Tidyが利用できればこんなことはもっとお手軽に(リソースの消費も少なく高速に)できちゃうわけで。しかし、HTMLParserの趣旨にも書いたように、現在の一般的なレンタルサーバのPHP環境ではこれは殆ど利用できない筈なので、そういう場合には利用価値があるやもしれませんが(このライブラリでは、Tidyが利用可能ならそちらを、そうでなければ(自動的に)その代替にHTMLParserを利用するようになってたりはします)、そんな感じのツクリなので、気に入らないところがあれば、しょせん既存のライブラリの組み合わせなので、わざわざオーバーライドするよりは自分好みの仕様で作り直しちゃうことをお勧め申し上げる次第でございます。

Category: ウェブ制作
Posted 2007年05月26日 00:25

2007年03月25日

ながらTube

Flash Media Playerで遊ぼう企画の第3弾くらい。再生するプレイリストを自動生成しようというネタ。Flashビデオが大量に転がってるところといえばあそこなわけで。そのAPIでプレイリストを作ればいんじゃん?という安直な企画デスネ。

で、出来たのが以下。

» ながらTube

上のリンク先のページの右側にプレイヤーが表示されると思いますが、そのままではいまいち面白くなさそうなビデオがリストされてるかと思います。
で、その左側にある入力フォームでキーワード検索ができるので、ここに好みのキーワード、例えば「浦和レッズ」とか入れてみてください。配色も変えられるので、「Background」を赤に変更してみましょう。「Foreground」と「Rollover」は白と黒の組み合わせがいい感じではないかと。最後に「Update」ボタンをクリックすると、プレイリストの内容とプレイヤーの見栄えが変わるという寸法です。

ながらTubeバッジ

これまで見てきた、ページの右側に出てくるヤツは、ウェブやブログに貼り付けることを想定した「バッジ」版なのですが、一方で下図のように、これをサブウィンドウで表示して、デスクトップで鑑賞する用途を想定した「バー」版もあります。
ながらTubeの、バーの説明にある「Nagara Tube Bar (Bookmarklet)」というリンクをクリックすると、バッジ版と同じ内容が、下図のようにサブウィンドウに表示されます(タブブラウザではポップアップウィンドウの設定を変更する必要があるかもしれません。IE7ではタブブラウズの設定中の、ポップアップの発生時の動作を確認してください)。このリンクはブックマークレットにもなるので、ブックマークやツールバーにこのリンクを登録しておくと、以降はそれを選択するだけで同じ設定のバーを表示できます。

ながらTubeバー

さて。

とか書いときながら実際にブログとかに貼られまくられちゃったりするとそれなりの負荷になるわけで。いつものようにソースを晒してるので、ご利用は自鯖でどーぞ。ただし、自作の部分はいつも通りフリーソフトウェアですが、Flash Media Playerはそうではなく、特に商用利用の場合はライセンス料金が必要になる点ご注意ください。

Category: ウェブ制作
Posted 2007年03月25日 22:58

2007年03月09日

“Reason for Living” - Roddy Frame (a.k.a. Aztec Camera)

ここにはFlashオブジェクトが表示されます。ここにFlashオブジェクトではなくこのテキストが表示されたままの場合は、以下の原因が考えられます。

  • RSSリーダーとかで表示している。
  • ウェブブラウザがDOM対応のものではないか、JavaScriptが無効になっている。
  • Flashプレイヤーがインストールされていない。
  • おいらの手違い。

お送りしている曲は、アズテックカメラことロディ・フレームの1998年のスリーコードチューン、「Reason for Living」です。

で、これは字幕作成のテスト。

歌詞が字幕で表示されるのって好きなんですよ。歌われる言葉は音楽の一部なので、ともすれば聞き流してしまいがちなのですが、書かれた言葉を目にすることでそれをしっかりと捉えられる気がするのです。殊に僕らの言語が採用している表意文字では文字そのものが具体的なイメージを喚起するので、なおさらそう思えるのかもしれません。

A little something about the tune

Technical things

Category: ウェブ制作
Posted 2007年03月09日 20:10

2007年03月06日

野菜小屋の地図をGoogle Maps APIで作ってみるテスト

野菜小屋

野菜小屋

京王線飛田給駅より徒歩30秒

営業時間や取扱商品などの詳細はblog.livedoor.jp/tokyo_la12でご確認ください。

飛田給駅の利用者なら、表通りでも派手に宣伝しているので大丈夫でしょうが、そうでなければ知らないひともいるかもしれないので、野菜小屋の地図を用意してみますた。

という口実で、GoogleマップのDOM APIで遊んでみたテスト。以下はトーキョーとは無関係。

» 「野菜小屋の地図をGoogle Maps APIで作ってみるテスト」の続きを読む

Category: FC東京, ウェブ制作
Posted 2007年03月06日 22:49

2007年01月12日

携帯JavaアプリのFlickrビューアがお蔵入りになった件

Pocket Flickr - a Flickr viewer for Mobile Java Pocket Flickr - a Flickr viewer for Mobile Java Pocket Flickr - a Flickr viewer for Mobile Java

信頼すべき情報筋によれば、“ポケットflickr”というコードネームで昨年末にコソコソ製作されていた、携帯電話向けJava実行環境用の、Flickrのビューア・アプリケーションがお蔵入りになった模様。

Eclipse

製作者はお蔵入りの理由について、「飽きたから」と語っているという。参考までに、製作者の血液型はB型とのこと。

ポケットflickrは、Flickrの写真を、Interestingnessや、タグ、フリーテキストなどで検索して、携帯電話で閲覧するビューア。表示した写真はJavaの保存領域に保存しておくことができ、待ち受けアプリなどとしても楽しめるなどの、よくありがちな機能が予定されていた。
なお、携帯電話のカメラで撮影した写真をアップロードする機能は、「メールでやればいいじゃん」ということで、そもそも検討されていない。

ポケットflickrは、クライアント用のJavaアプリケーションと、FlickrのAPIとの通信や、携帯電話向けの写真の加工、及びこれらのキャッシュ処理などを行うウェブサーバ用のアプリケーションで構成され、その最初のバージョンは、NTTドコモのFOMAシリーズ用iアプリとして2006年内にリリースされる予定であった。

なお、製作が再開される見込みについては、「ないない」とのこと。

このエントリーの画像には、idfontで公開されている「id-カナ007」フォントを使用しています。このフォントの著作権は、井上デザイン・井上優さんが所有しています。

Category: ウェブ制作
Posted 2007年01月12日 23:04

2007年01月11日

heywatch.comどうでしょう

Hey! Watchは、各種の動画変換(再エンコード)を行う、マルセイユ発のウェブサービスでございます。
動画変換のウェブサービスなんてえものは既に多々あるわけですが、TechClunchの記事に、メインの機能はREST APIでディベロッパー向けに公開し、サービスに直接に構築できるよう配慮している(ちなみにヘンな日本語は原文のママ、とゆうかTechClunchの日本語はしばしばヘン)と書かれてたので触ってみますた。

Hey! Watchは、現在は本サービスに移行していますが、つい先日までは「invitation only beta」でした。以下のハナシは原則、その頃のものです。始まったばかりのサービスなので内容はどんどん変更されていく筈で、現状は違ってる可能性がたぶんにあることにご留意ください。

基本的な使い方はいたってシンプル。

  1. 変換したいビデオを、ローカルからアップロードするか、URLで指定する。
    指定できるURLは:
    • ビデオファイルのURL。
    • ビデオファイルのURLが書かれたウェブページのURL(複数ある場合はあらためて選択する)。
    • YouTubeやGoogleビデオなど、対応している動画配信サービスのウェブページのURL。
  2. ビデオがいったんHey! Watchに転送されるので、完了するのを待つ(転送状況は随時確認できる)。
  3. 転送が完了したら、変換したいフォーマットを選んでエンコードする。
    ただし、Hey! Watch全体で、同時に行えるエンコード数には制限がある。これは「スロット」という単位で表現されており、「空きスロット」があれば直ちにエンコードが行われるけれど、そうでなければ待たされる(エンコードの状況も随時確認できる)。

アカウントには、無料の「Free」と有料の「Watcher」(なんでそういう名前なのかは不明)があり、Watcherアカウントは$4.99/月で、「まとめて払えばお得」なプランもあり。ちなみに支払い方法は、現時点ではPayPalのみ(イケてないところ、その1)。
ふたつのアカウントの違いが具体的に書かれたページはログインしないと見れないので、以下にサクっとまとめてみた。

制限Free(無料)Watcher($4.99/月)
1ヶ月間に可能なエンコード回数10回100回
エンコードフォーマットのカスタマイズNGOK
転送した(オリジナルの)ビデオの保存期間6時間12時間
エンコードしたビデオの保存期間12時間24時間
同時に可能な転送数13
同時に可能なエンコード数13
ビデオあたりの再生時間の制限10分25分
ビデオあたりのデータサイズの制限なしなし
転送量の制限なしなし

ちなみに、このページに限らず、ヘルプですらログインしないと見れません(イケてないところ、その2)。

APIどうでしょう

ウェブインタフェースは非常にクールで直感的だが、REST APIも同様に直感的なものだ。ただし、現在のAPIドキュメントは、必要最低限の内容しか用意されていないので、APIドキュメントの解読は直感的というわけにはいかない(笑)。そして、ご多分に漏れずこのドキュメントも、やはりログインしないと見れません。見たければユーザ登録するアルよ。

Hey! Watchのサービスはユーザ認証が前提なので、APIアクセスもユーザ認証が必須ですが、その認証方式は、To use our API, you must be authenticated via Basic Auth.ということで、ベーシック認証でございます。HTTPSでもないので、パケットでIDとパスワードが丸見えなんだな、これが(イケてないところ、その3)。

基本的な仕様はごく普通の(素直な)REST API。
POSTでリソースを作成し、GETで情報取得、PUTで変更、DELETEで削除。

例えば、YouTubeのFLVを変換するなら、/discoverにページのURLをPOSTしてFLVのURLを検出させる。

POST /discover.xml HTTP/1.0
Authorization: Basic hoge
Host: heywatch.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 58

url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DBPsfbV9t8Vw

POSTのレスポンスではまだ検出が完了しているわけではないので、同じURLを適宜GETして検出の完了を確認する。

GET /discover.xml HTTP/1.0
Authorization: Basic hoge
Host: heywatch.com

で、検出が完了したXMLレスポンスはこんなんで、RESULT要素の中身がFLVのURL。
どうでもいんだけど、CREATED-AT要素のタイムゾーンがお国柄を匂わせてたりします。

<?xml version="1.0" encoding="UTF-8"?>
<discovers>
  <discover>
    <id>12345678</id>
    <status>ok</status>
    <download>false</download>
    <result>
      http://www.youtube.com/get_video?video_id=BPsfbV9t8Vw&l=61&t=OEgsToPDskI5F9Jp4wgYUqAzjmkPJN7D
    </result>
    <url>
      http://www.youtube.com/watch?v=BPsfbV9t8Vw
    </url>
    <title>
      YouTube - YouTube Happy New Year
    </title>
    <created-at>
      Mon Jan 01 00:00:00 +0100 2007
    </created-at>
  </discover>
</discovers>

で、検出が完了したらそのURLを、今度は/downloadにPOSTして、Hey! WatchにFLVを転送開始。同様に適宜GETして転送の完了を待ち、完了したら今度は/jobに、そのビデオのIDとエンコードフォーマットのIDをPOSTしてエンコード開始…。

まあ、そんな感じ。

レスポンスの形式は、XMLの他にJSONとYAML。この指定は、APIのリクエストURLに対応する拡張子を付けて行います。例えば、エンコード済みのビデオの一覧を取得する場合、XMLなら「GET /encoded_video.xml」、JSONなら「GET /encoded_video.js」という感じ。
なお、リクエストによっては特殊なレスポンスが用意されていて、例えば「GET /encoded_video.rss」とすると、エンコード済みのビデオの一覧のRSSフィード(Podcastコンパチ)が出てきたり、「GET /encoded_video/video_id.jpg」とすると、エンコード済みのビデオのサムネイル画像が出てきたりします。

リクエスト結果は、まずはHTTPステータスコードで判断するわけですが、ドキュメントにあるのは、200 OK、201 Created、204 No Content、400 Bad Request、404 Not Found、406 Not Acceptable。
どうでもいんだけど204は、means that the resource has been updated or deleted successfullyと書かれていて、おそらくPUT/DELETEの成功を示すのだろうけど、ステータスコードの本来の意味からするとヘン。
一方406は、the format you asked is not availableと書かれていて、この「format」はおそらくエンコードフォーマットのことなのだと思うけど、だとしたら、ステータスコードの本来の意味の履き違え。

携帯どうでしょう

Hey! Watchのエンコードフォーマットには「Mobile 3GP」や「Mobile 3GPP2」、「Mobile MP4」といった選択肢があり、また、Watcherアカウントであればフォーマットのカスタマイズも可能で、例えば「Mobile 3GP」のビデオコーデックはH.263なのだけど、これをMPEG4に変更したりできる。

これらのフォーマットに変換したビデオは、日本の携帯電話でも再生できる場合もある。ただし、Hey! Watchはおそらく、LinuxプラットフォームでFFmpegを使ってエンコーディングを行っており、FFmpegでのエンコード時の問題がそのまま残される(例えばドコモ端末でストリーミング再生できないとかいう類)。

これについてHey! Watchの中のひととメールでやり取りしてみますた(ちなみにレスポンスはめちゃめちゃ迅速でした)。
指定のデータサイズでビデオを分割できるようにして欲しいなあという要望は、やんわりと、しかし即座に却下。3GP_Converter(aka 携帯動画変換君)のQT3GPPFlattenの例を挙げて、Atomフィールドの書き換えとかやって欲しいなあという要望を出してみたら、こっちはうやむやのうちに却下(笑)。
唯一、指定のデータサイズになったらエンコード糸冬了という機能だけ検討してくれることになりますた。って、それってFFmpegに-fsパラメータ渡すだけじゃん。

Category: ウェブ制作
Posted 2007年01月11日 19:10

2006年12月26日

第一次ハラ東京最後の日

天皇杯のハナシを書いてて思い出した、第一次ハラ東京最後の試合となった、松山での天皇杯レッズ戦で撮影していたビデオを公開しておこうかと。

試合後の挨拶

試合後の挨拶

ガスサポの行進

ガスサポの行進

というフリして、動画をYouTubeにアップしてみたテスト。
以下はテストのメモ。もはやハラ東京とは無関係。

» 「第一次ハラ東京最後の日」の続きを読む

Category: FC東京, その他のスタジアム, ウェブ制作, 愛媛のお勉強
Posted 2006年12月26日 18:43

2006年12月15日

Galicia - Sí, es única

ここにはFlashオブジェクトが表示されます。ここにFlashオブジェクトではなくこのテキストが表示されたままの場合は、以下の原因が考えられます。

  • RSSリーダーとかで表示している。
  • ウェブブラウザがDOM対応のものではないか、JavaScriptが無効になっている。
  • Flashプレイヤーがインストールされていない。
  • おいらの手違い。

久しぶりにルアル・ナ・ルブレ Luar na Lubre のウェブサイトが更新され、なにかと思いきや、YouTubeにアップされてる自分たちのビデオへのリンク集というネタで。TVGで放映されたスタジオライブとか、旧作のビデオクリップとか、日本のルブレオタ(推定3人程度)にとっては垂涎の映像が拝めるわけですが、これらはもちろん“オフィシャルに”アップロードされたものなわけはなく。うちらの国なら、アーティスト本人より先にJASRACあたりが、「削除と謝罪と賠償とユーザーの氏名・住所の登録制を要求するニダ」と騒ぎ立てるんだろうなと思ったり。

さて、上のFlashな動画はTurgalicia(ガリシア観光局)が今年の夏に制作したテレビCM集です。
「Tu Gitana」が最初のふたつのもののBGMに採用されているため、ルアル・ナ・ルブレのウェブサイトでも紹介されていますが、いかんせん本家の動画(左のメニューね)を日本から眺めようとすると、ダウンロードにドえらい時間がかかる上に、一部HTMLがクズなのでまともに見れやしない。てなことで、ガリシアへのニッポン人観光客誘致の一助になればという願いをこめて…。

というフリをしてFlashビデオをあれこれいじってみたテスト。
以下はテストのメモ。もはやガリシアとは無関係。

» 「Galicia - Sí, es única」の続きを読む

Category: ウェブ制作, ガリシアのお勉強
Posted 2006年12月15日 21:54

2006年11月22日

ぜろちゃんねる(0ch BBS Script)掲示板のスパム対策

1ヶ月ほど前から、うちのぜろちゃんねる掲示板に英文スパムが来襲するようになり、当初はDNSブラックリスト(DNSBL/RBL)で対処してたのですが、プロのスパム屋は新しいプロキシを次々と用意してくるわけで、ブラックリストがすべてを網羅できるわけはなく。それでも数が少ないうちはコマメに消してたのだけど、日増しにその数が増えて、やってらんねえ状態になったので、こんなふうにしてみますた、というメモ。

やってることは、2ちゃんねる BBQなどの任意のDNSBLサービスに登録されてるホストからの投稿を弾くのと、日本語文字(Shift_JISの2バイト文字)が本文に含まれない投稿を弾くこと(いわゆるひとつのBanAscii)。後者はURLだけのカキコとかも弾いてしまうわけですが、まあしょうがない。

ぜろちゃんねるスクリプトの改造は正直かなりマンドクセー('A`)わけですが、これなら改造が1箇所で済むし簡単な処理なのだけど、結構効果的なのではなかろうか、と。

追記@2007/01/30

# 0ch BBS Scriptの2005/11/13、2006/02/27、2007/01/24版で確認

# test/module/vara.plファイル内の
# IsRegulation(規制チェック)サブルーチン(2007/01/24版なら296行目あたり以降)内の
# 適当なところ(例えば「# 規制ユーザ・NGワードチェック」とある行の前)に
# 以下の「...」に囲まれている箇所を追加

# エラー番号は既存のものを流用なのでエラーメッセージも既存のもののままです
# それではワケワカランと思われる場合は、test/info/errmsg.cgiに新しい番号とメッセージを追加し
# 以下の「return」の後に続く番号をそれと取り替えるとモアベター

sub IsRegulation
{

...

  # 本文にShift_JISの2バイト文字が含まれていなければ
  # 600エラー(NGワード)

  if ($this->{'FORM'}->Get('MESSAGE') !~ /[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]/) {
    return 600;
  }

  # 本文に<a~>または[url=や[url]が含まれていれば
  # 600エラー(NGワード)

  if ($this->{'FORM'}->Get('MESSAGE') =~ /(<a\b[^>]*?>|\[url(?:\s?=|\]))/i) {
    return 600;
  }

  # メール欄に@が含まれていれば
  # 600エラー(NGワード)

  if ($this->{'FORM'}->Contain('mail','@')) {
    return 600;
  }

  # IPアドレスが以下で指定するDNSBLのいずれかに登録されてれば
  # 997エラー(PROXY規制)
  # ブラックリスト鯖はお好みで

  my @dnsbls = (
    'niku.2ch.net',
    'bsb.spamlookup.net',
    'bbx.2ch.net'
  );

  $ENV{'REMOTE_ADDR'} =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
  foreach my $dnsbl_host (@dnsbls) {
    $dnsbl_host = "$4.$3.$2.$1.$dnsbl_host";
    if (gethostbyname($dnsbl_host)) {
      return 997;
    }
  }

...

}

で、昨日これを仕込んで以降24時間のうちに、以下のようなホストがスパムの投稿にやってきたわけですが、幸いすべて弾けた模様。エラーが103となってるものは「本文長すぎ」で、これは規制チェックより前に実施されるので、これに引っ掛かるヤツもいるのだけど、これで既に結構な数が弾けている。で、600エラーが本文に日本語文字が含まれなかったもの。ここまでですべて弾けてしまい、結局DNSBLのチェックにまではどいつも至ってなかったり。

来襲時刻エラーホスト名#1#2#3#4#5
11/21 20:29103c-71-230-86-217.hsd1.pa.comcast.net     US
11/22 19:13600bzq-84-110-225-57.red.bezeqint.net     IL
11/22 19:13600bzq-84-110-236-82.red.bezeqint.net     IL
11/22 19:12600bzq-84-110-231-119.red.bezeqint.net     IL
11/22 19:12600bzq-84-110-233-25.red.bezeqint.net     IL
11/22 16:4810382.137.247.132yes    SY
11/22 15:00103www.farmington.k12.mi.usyes    US
11/22 13:2210384-123-102-217.onocable.ono.com     ES
11/22 12:11600200.29.137.217yes   yesCL
11/22 12:10600218.236.45.155yesyesyes  KR
11/22 12:05600S01060040ca37ccf6.cn.shawcable.netyesyes   CA
11/22 12:05600avquarantine.sec.kq.noyesyes   NO
11/22 11:23103proxy4-jan.integrity.comyes    US
11/22 10:1710389-149-205-145.internetserviceteam.com yes   DE
11/22 09:51600218.236.111.150yesyesyesyes KR
11/22 09:3710382.137.247.131yes    SY
11/22 07:5010394.190.189.72.cfl.res.rr.com     US
11/22 06:21600ool-44c0fe41.dyn.optonline.net     US
11/22 06:02103adsl-75-52-193-66.dsl.chcgil.sbcglobal.netyes    US
11/22 04:11600bzq-84-110-226-237.red.bezeqint.net     IL
11/22 02:27103ulbcenter-04-b07.it-datacntr.louisville.edu     US
11/22 01:08103riy01che02.ae.net.sayesyesyes  SA
11/22 00:3810364.8.149.34     US
11/21 23:56600bzq-84-110-251-251.red.bezeqint.net     IL
11/21 23:56600bzq-84-110-252-72.red.bezeqint.net yes   IL
11/21 23:55600bzq-84-110-241-44.red.bezeqint.net     IL
11/21 23:31600220.119.158.14yesyesyes  KR
11/21 23:3060086.35.202.101yes yes  RO
11/21 23:29600211.207.215.84yes yes  KR
11/21 23:25600210.113.30.208yesyesyesyesyesKR
11/21 22:5310363-109-248-28.reverse.newskies.netyes yesyes US
11/21 21:1510389-149-205-145.internetserviceteam.com yes   DE

上表の「#1~5」の欄は、各ホストについて以下の各DNSBLサービスでの登録の有無です。

  1. 2ちゃんねる BBQ - niku.2ch.net
  2. SpamLookup - bsb.spamlookup.net
  3. The Spamhouse Project - sbl-xbl.spamhaus.org
  4. SpamCop.net - bl.spamcop.net
  5. RBL.JP - all.rbl.jp

ご覧の通り、どのブラックリストにも登録されていないホストが結構あり、プロのスパム屋に狙われたら、やっぱしDNSBLでの規制だけでは不十分なんだよね、ということですな。

なお、2ちゃんねる BBQはDSBLでの登録状況も併せて応答するので、上表ではDSBLは端折ってます(なので、このふたつを併用するのは無意味です)。また、ちなみにどのホストも、2ちゃんねる BBX(bbx.2ch.net)には登録されていなかったので、こちらも上表では端折ってます。

参考になったり興味深く拝見したページ

» 「ぜろちゃんねる(0ch BBS Script)掲示板のスパム対策」の続きを読む

Category: ウェブ制作
Posted 2006年11月22日 23:38

2006年10月20日

Googleカレンダーを携帯で利用するPHPスクリプト

これは自作のGData API用クラスライブラリのデモとして制作した、携帯電話向けにGoogleカレンダーの表示と編集を行うウェブアプリケーションです。
しかしそもそも、携帯電話の画面にHTMLだけで、カレンダー/スケジューラのまともなインタフェースを作るのは結構無理難題なわけで。いちいち通信が必要なウェブアプリじゃあ、操作性も決してよろしくなく。携帯電話でGoogleカレンダーを利用する理想形は、やっぱ、Googleカレンダーとデータを同期するJavaアプリとかだと思うのですよ。実際iアプリなどには優れたカレンダー/スケジューラが既に多くあり、これらがこういう機能を備えれば、こんなウェブアプリはそりゃもう用無しだ罠。

予定リストの表示 カレンダー形式の表示 予定の詳細 予定の編集

PC/PDAで、このスクリプトのオンラインデモを確認できます。
このデモでは、アカウントの認証をこのアプリの本来の方式ではなく、AuthSubという方式で行います。この認証方式は、iモードやEZwebなどの携帯電話ブラウザからは利用できません。またGoogle Apps for Your Domainのアカウントも利用できません(当然ですが、このアプリの本来の方式では、いずれも利用できます)。

アーカイブのダウンロード

» gcal.zip 0.9.5 (eternally beta) issued 2007-06-06

機能制限

以下は現時点でGoogle側に不具合があるための制限です。追記参照

動作要件とか

設置手順

このアプリ自体の認証は、携帯電話の端末ID(製造番号とかサブスクライバIDとかシリアル番号とか)とパスコード(暗証番号)で行います。(端末IDが送信される状態で)ログインフォームに暗証番号を入力するだけという、ケータイフレンドリーな認証ですね。
そういうわけで、最初に設定ファイル(Config.inc.php)に、端末IDとパスコード、そしてGoogleアカウントのメールアドレスとパスワードを登録しておく必要があります。

  1. アーカイブ内の各ファイルをサーバの任意のディレクトリにアップロードし、「data」ディレクトリのパーミッションを707など、書き込み可能なものに変更します。
  2. 携帯ブラウザで_check_auth.phpにアクセスし、パスコードを入力して表示される内容をメールなどでPCに送ります。この内容を、Config.inc.phpファイルの「$auth_db」とある箇所に登録してください。「SERIAL」と表示される内容を$auth_dbの「serial」欄に、「PASS」と表示される内容を$auth_dbの「password」欄に、それぞれ登録します。
  3. 引き続き$auth_dbの、「google_username」欄にGoogleアカウントのユーザ名(@記号以降のドメイン名までを含むメールアドレス)を、「google_password」欄にそのパスワードを登録し、Config.inc.phpファイルをサーバにアップロードします。
  4. PCや携帯のブラウザでgcal.phpにアクセスして、期待通りに動作するか確認してみてください。この時点ではまだ、認証はパスコードのみで、端末IDでの認証は行っていないので、PCのブラウザでもアクセスできる筈です。
  5. 最後に、Config.inc.phpファイルの冒頭にある「define('GCAL_USE_DEVICE_ID_FOR_AUTHENTICATION', true);」とある行の、先頭のコメント記号(ふたつのスラッシュ記号)を削除し、これをサーバにアップロードしてください。これで、認証時に端末IDも照合されるようになり、端末IDを登録した携帯電話以外からはアクセスできなくなります。

gcal.phpのファイル名は任意のものに変更できます。設定などは不要です。
HTML表示を変更するにはTemplate.htmlを編集してください。各種の設定はConfig.inc.phpで行います。

ありがちなトラブル

スクリプトにアクセスするとHTTP 500 Internal Server Errorになる。
アーカイブ中の.htaccessファイルをサーバにアップロードしている場合は、この. htaccessの記載内容による設定変更が許可されていないことが原因かもしれません。.htaccessファイル中の「php_flag」で始まる行を削除して、再度試してみてください。ただこの場合、サーバのPHPの設定次第では、このスクリプトは期待通りには動作しないかもしれません。
日時が日本時間じゃない?
PHP4ではスクリプトが明示的にタイムゾーンを設定することができないため(できたっけ?)、日時はスクリプトが動作しているサーバのタイムゾーンに沿ったものとして表示されます。例えば海外のレンタルサーバなどでPHP4の場合は、日時が現地のタイムゾーンで表示されるかもしれません(適切なタイムゾーンが設定されていないサーバも同様です)。逆に、Googleカレンダーのタイムゾーンが海外に設定されていても、国内のサーバでは、PHP4なら日本時間で表示されることでしょう。
PHP5では、設定ファイルで、定数GCAL_TIMEZONEの値に指定したタイムゾーンのIDに沿ったものになります。このIDは、例えば「Asia/Tokyo」や「America/Los_Angeles」など、タイムゾーンのリストにあるものです。

ライセンスとか

このプログラムの本体であるgcal.phpファイルはGNU GPL v2+でライセンスされますが、このプログラムは、そそくさと制作を済ませるために既存のオープンソースソフトウェアをあれこれ流用しており、かつ簡単に設置できるように、アーカイブにはこれらのファイルも含まれてるかもしれません。こうしたソフトウェアのライセンスは、そのファイルに記載のものとなり、gcal.phpとは別にライセンスされる点ご注意ください。

» 「Googleカレンダーを携帯で利用するPHPスクリプト」の続きを読む

Category: ウェブ制作
Posted 2006年10月20日 00:00

2006年10月04日

Google Calendar APIの基礎

当初はGoogle自身がPHP用のライブラリを用意する予定だと表明していましたが、結局10月の終わりになって、Zend Google Data Client Libraryが登場しました。てなことで、PHP5をご利用の場合、以下のクラスライブラリは無用の長物と化しましたので、そこはすっ飛ばして、カレンダーAPIの解説としてご利用ください(ZendのものはPHP5用なので、PHP4をご利用の場合には、以下のクラスライブラリにも利用価値はあるかもしれません)。
なお、Googleカレンダーの携帯向けウェブアプリは元々このクラスライブラリのデモとして作ったものなので、このまま放置の予定。


PHP 4/5 class library for Google Data APIs

これは、GData APIとの通信を処理する、PHP4/5用のシンプルなクラスライブラリです(フィードの解析や作成を行う機能は提供されません)。詳細はREADMEをご覧ください。

» ZIPアーカイブのダウンロード

アーカイブの内容
  • Google
    • Account.php - アカウント認証API用クラス
    • GData.php - GData APIの共通クラス
    • Calendar.php - カレンダーデータAPI用クラス
    • Blogger.php - BloggerデータAPI用クラス
  • test.php - テストアプリその1
  • gbase_demo_alt.php - テストアプリその2
  • XMLParseIntoStruct.php - XMLを配列に処理する汎用のSAXパーサ(おまけ)
  • README

テストアプリその1(test.php)は、以下で説明するふたつのアカウント認証API(ClientLoginとAuthSub)のそれぞれを使ってカレンダーフィードを取得する、シンプルなものです。
テストアプリその2(gbase_demo_alt.php)はGoogle Baseで料理レシピの投稿と編集を行うものです。これは、Google自身による、Google BaseデータAPIのPHP版デモを、このクラスライブラリを使って書き直したもので、これを動作させるにはAPIキーを取得して、このスクリプトの冒頭に登場する定数「GOOGLE_BASE_DEVELOPER_KEY」の値にそれを設定してください。


目次

GoogleカレンダーデータAPIへのアクセスは、公開予定の取得、及び「magicCookie」と呼ばれるURLを利用した非公開予定の取得については認証は不要ですが、予定の追加、変更、削除には認証を要します。
ここでは最初にアカウント認証について、次いでカレンダーデータAPIについてまとめています。

Google Account Authentication

  1. アカウント認証の概要
  2. ClientLogin
  3. AuthSub

Google Calendar Data API

  1. カレンダーデータの取得
  2. イベントの追加
  3. イベントの変更と削除

アカウント認証の概要

認証を要するGoogleの各サービスAPIへのリクエストを行うアプリケーションは、ユーザIDとパスワードそのものではなく、アカウント認証APIによって発行される、トークン(token)と呼ばれるその代替データを送信します。トークンはその有効範囲内で繰り返し利用可能なので、アプリケーションはアクセスの都度トークンを取得する必要はありません。

アカウント認証APIにはClientLoginAuthSubがあります。

AuthSubでは、ウェブアプリケーションは、ユーザをGoogleアカウントのウェブサイトに誘導して、アプリケーションに対するアクセスの許可を依頼します。このときのURLには、「next」パラメータに、AuthSubからのレスポンスを処理するURLを指定しておきます。
以下の画像は、ユーザに表示されるログインフォームとアクセス要求の確認フォームの例です。アクセス要求の確認フォームでユーザが「アクセスを許可」をクリックすると、「next」パラメータで指定したURLに対してリダイレクトが行われます。

Googleアカウント - お客様のアカウントにサードパーティのサービスがアクセスできるようにするには、ログインする必要があります。 Googleアカウント:アクセス要求 - www.abcd.comがGoogleカレンダー アカウントにアクセスを要求すると、代わりにGoogleアカウントにアクセスすることができます。[アカウント情報]から、いつでもアクセスを取り消すことができます。www.abcd.comがお客様のパスワードおよび個人情報を入手することはありません。
注意

なお、AuthSubのアクセスの確認フォームでは、アプリケーションがGoogleに登録されたものではない場合、以下のような警告文が表示されます。

このウェブサイトはGoogleに登録されていないため、承認リクエスト用の保護された接続を確立できません。
下記のリンク先を信頼できる場合にのみ、処理を続行してください。

登録済みのアプリケーションでは、この警告文は表示されません。また、登録済みのウェブアプリケーションはセキュアなトークンを利用可能ですが、これはここでは省略します。


ClientLogin

ClientLoginでは、https://www.google.com/accounts/ClientLoginに対して以下のようなリクエストを送信します。

POST /accounts/ClientLogin HTTP/1.1
Content-Type: application/x-www-form-urlencoded

Email=Email&Passwd=Passwd&service=service&source=source

ポストデータの内容は以下の通りです。

キー
Email ユーザID(ドメイン名を含むメールアドレス)
Passwd パスワード
service 対象サービスのID(Googleカレンダーの場合は「cl」)
source アプリケーションのID(任意の文字列で、「企業名-アプリケーション名-バージョン」形式が推奨されています)
logintoken CAPTCHAチャレンジのトークン(CAPTCHAチャレンジ以外では省略)
logincaptcha CAPTCHAチャレンジの回答(CAPTCHAチャレンジ以外では省略)

リクエストが成功した場合のレスポンスはHTTP 200 OKで、そのボディは、以下のような「キー=値」形式のテキストです。このうち「Auth」キーの値がトークンです。他の値は現時点では使用されていません。

SID=DQAAAGgA...7Zg8CTN
LSID=DQAAAGsA...lk8BBbG
Auth=DQAAAGgA...dk3fA5N

なお、リクエストが不成功の場合、そのボディが、以下のような「キー=値」形式のテキストで、「Error」キーの値が「CaptchaRequired」である場合があります。これは、ClientLoginがロボットなどによる不正侵入の可能性を疑い、CAPTCHAチャレンジ(画像認証)を要求しているものです。

Error=CaptchaRequired
CaptchaToken=DQAAAGgA...dkI1LK9
CaptchaUrl=Captcha?ctoken=DQAAAGgA...dkI1LK9

この場合、アプリケーションは、「CaptchaToken」キーに示された値と、「CaptchaUrl」キーに示されたURLで表示されるCAPTCHAチャレンジ画像の回答を、それぞれ「logintoken」と「logincaptcha」の値としてポストデータに追加し、ClientLoginへのリクエストを再度行うことになります。

クラスライブラリを使ったサンプルプログラム

以下の例は、この文章の冒頭で紹介しているPHPクラスライブラリを利用して、ClientLoginでトークンを取得し、それを表示するものです。

<?php
$email = 'example@gmail.com';
$password = 'password';

require_once 'Google/Calendar.php';
$service = new Google_Calendar;

session_start();
if (!isset($_SESSION['tokens'])) {
  $_SESSION['tokens'] = array();
}
if (!isset($_SESSION['tokens']['calendar'])) {
    if (!$service->requestClientLogin($email, $password)) {
    exit("ClientLogin has failed.\n".$service->getResponseBody());
  }
  $_SESSION['tokens']['calendar'] = array(
    $service->token,
    $service->authType
  );
}
var_dump($_SESSION['tokens']);
?>

ここでは、取得したトークンはいったんPHPのセッションデータに保存しています。トークンは繰り返し利用可能なので、アプリケーションがサービスAPIに複数回のリクエストを行う予定なら、トークンをPHPのセッションデータに保存しておくことで、リクエストの都度トークンを取得せずに、保存してあるものを利用することができます。この場合、実際にAPIにリクエストを行う際には、トークンを取得したアカウント認証APIがClientLoginかAuthSubかによって送信内容が異なるので、トークンには、それを取得したアカウント認証APIの種類(このクラスではauthTypeプロパティ)も紐付けておきます。
また、トークンは各サービスAPIごとに異なるので、アプリケーションが複数のサービスを利用する場合は、上の例でハッシュのキーにサービス名を用いているように、それがどのサービス用のものかを識別できるようにしておきます(AuthSubでは、AuthSubTokenInfoによってトークンの対象サービスを確認することが可能です)。


AuthSub

Googleアカウント:アクセス要求 - www.abcd.comがGoogleカレンダー アカウントにアクセスを要求すると、代わりにGoogleアカウントにアクセスすることができます。[アカウント情報]から、いつでもアクセスを取り消すことができます。www.abcd.comがお客様のパスワードおよび個人情報を入手することはありません。

AuthSubではトークンは、Googleアカウントのウェブサイトで表示されるアクセス要求の確認フォームで、ユーザが「アクセスを許可」をクリックすることで発行されます。
ユーザはまず、https://www.google.com/accounts/AuthSubRequestに、以下のパラメータをクエリー文字列に指定したURLにアクセスします(ウェブアプリケーションはユーザにこのURLを示して、アクセスの許可を依頼します)。

キー
next アクセスが許可された場合に行われるリダイレクト先のURL(AuthSubのレスポンスを処理するウェブアプリケーションのURL)
scope 対象サービスのURL(Googleカレンダーの場合は「http://www.google.com/calendar/feeds/」)
session 使い捨てトークンをセッショントークンと交換するか否か

アクセス要求の確認フォームで、ユーザが「アクセスを許可」をクリックすると、AuthSubは「next」パラメータで指定されたURLに、「token」パラメータを付加してリダイレクトします。
この「token」パラメータの値は使い捨てトークン(one-time|single use token)です。これは一回限り有効で、繰り返し利用することはできませんが、上表にある「session」パラメータの値が「1」の場合、この使い捨てトークンは、繰り返し利用可能なセッショントークン(multi-use session token)と交換することができます。

使い捨てトークンをセッショントークンと交換するには、https://www.google.com/accounts/AuthSubSessionTokenに対して以下のようなリクエストを送信します。Authorizationヘッダで指定するtokenは使い捨てトークンの値です。

GET /accounts/AuthSubSessionToken HTTP/1.1
Authorization: AuthSub token="token"

リクエストが成功した場合のレスポンスはHTTP 200 OKで、そのボディは、以下のような「キー=値」形式のテキストです。このうち「Token」キーの値がセッショントークンです。他の値については、現時点では使用されていません。

Token=DQAA...7DCTN
Expiration=20061004T123456Z

なお、Googleに登録済みのウェブアプリケーションはセキュアなトークンを利用可能ですが、これはここでは省略します。

クラスライブラリを使ったサンプルプログラム

ユーザが、以下の例で示されるリンクをクリックして表示されるアクセス要求の確認フォームで、「アクセスの許可」をクリックすると、http://www.abcd.com/gcal/auth.php?token=tokenというURLにリダイレクトが行われます。このURLの「token」パラメータの値は、カレンダーデータAPIを対象とした、セッショントークンと交換可能な使い捨てトークンです。

<p>以下のリンクをクリックしてGoogleアカウントのウェブサイトに移動し
お客様のGoogleカレンダーにABCD.comがアクセスすることを許可してください。<br />
ABCD.comがお客様のパスワードおよび個人情報を入手することはありません。</p>

<p><a href="<?php
require_once 'Google/Calendar.php';
echo Google_Calendar::getAuthSubRequestUrl('http://www.abcd.com/gcal/auth.php')
?>">Googleアカウント アクセス要求</a></p>

そして以下の例では、使い捨てトークン(「token」パラメータの値)をセッショントークンと交換しています。なお、取得したトークンはClientLoginでの例と同様に、PHPのセッションデータに保存しています。

<?php
require_once 'Google/Calendar.php';
$service = new Google_Calendar;

session_start();
if (isset($_GET['token']) and strlen($_GET['token']) > 0) {
  if (!$service->requestAuthSubSessionToken($_GET['token'])) {
    exit("AuthSubSessionToken has failed.\n".$service->getResponseBody());
  }
  if (!isset($_SESSION['tokens'])) {
    $_SESSION['tokens'] = array();
  }
  $_SESSION['tokens']['calendar'] = array(
    $service->token,
    $service->authType
  );
  var_dump($_SESSION['tokens']);
}
?>

トークンの確認と廃棄

AuthSubの有効なトークンの発行数は、1ユーザ/1アプリケーションあたり10ヶまでに制限されています。そのため、取得済みの有効なトークンを廃棄(revoke)する必要が生じる場合があります。

https://www.google.com/accounts/AuthSubTokenInfoに対して以下のようなリクエストを送信することで、そのトークンが有効かどうかを確認できます。

GET /accounts/AuthSubTokenInfo HTTP/1.1
Authorization: AuthSub token="token"

指定したトークンが有効な場合のレスポンスはHTTP 200 OKで、そのボディは、以下のような「キー=値」形式のテキストです。

Target=www.abcd.com
Scope=http://www.google.com/calendar/feeds/
Secure=false

有効なトークンを廃棄するには、https://www.google.com/accounts/AuthSubRevokeTokenに対して以下のようなリクエストを送信します。

GET /accounts/AuthSubRevokeToken HTTP/1.1
Authorization: AuthSub token="token"

リクエストが成功した場合のレスポンスはHTTP 200 OKです。

なお、ユーザはGoogleアカウントウェブサイトの「アカウント情報」ページで、現在有効なAuthSubトークンの確認と廃棄が行えます。また、AuthSubのアクセス要求時に、既に制限数を超えるトークンが発行されている場合は、その旨のメッセージが表示されます。
以下の画像は、制限数を超えるトークンが発行済みの場合のメッセージと、トークンの確認と廃棄の画面です。確認と廃棄の画面で「アクセスを取り消す」をクリックしたトークンは廃棄され、無効になります。

Googleアカウント:アクセス要求 - Sorry, you have too many oustanding 3rd party sessions. Please revoke the access you've given to another 3rd party in order to continue. Googleアカウント:認証済みのウェブサイト - お客様のGoogleアカウントでは次のサービスにアクセスできます。

クラスライブラリを使ったサンプルプログラム

以下の例では、まずトークンが有効かどうかの確認を行い、有効な場合はそのトークンを廃棄しています。

<?php
$token = 'GD32CMCL25aZ-v____8B';

require_once 'Google/Calendar.php';
$service = new Google_Account;

if (!$service->requestAuthSubTokenInfo($token)) {
  echo("Token $token is not valid.");
} else {
  if (!$service->requestAuthSubRevokeToken($token)) {
    exit("AuthSubRevokeToken has failed.\n".$service->getResponseBody());
  }
  echo("Token $token has been revoked and is no longer valid.");
}
?>

カレンダーデータの取得

GoogleカレンダーデータAPIとアプリケーションは、Atom 1.0(またはRSS 2.0)に準じた、GDataと呼ばれるフィードデータをHTTP通信でやり取りします。

Googleカレンダーのフィードは、http://www.google.com/calendar/feeds/User_ID/Visibility/Projection[/Entry_ID|?Query_String]という形式のURLにアクセスすることで取得しますが、イベントの追加、変更、削除を行うアプリケーションでは一般的に、http://www.google.com/calendar/feeds/default/private/fullに対して以下のようなリクエストを送信して、読み書きのいずれにも利用可能な、非公開予定を含むフィードを取得します。

GET /calendar/feeds/default/private/full[/Entry_ID|?Query_String] HTTP/1.1
Authorization: [GoogleLogin auth=ClientLogin_token|AuthSub token="AuthSub_token"]

URLに以下の検索パラメータをクエリー文字列に指定することで、条件付きでフィードを取得できます。
なお、日時は、Atom 1.0が採用しているRFC3339形式で指定します。

キー
q 文字列の検索式(Googleのウェブ検索などと同様、「"」、「|」、「-」などのオペレータが使用可能です)
max-results 最大取得件数(省略時:25件)
start-index 取得開始エントリーの索引番号(省略時:1)
orderby エントリーの並び順(省略時:更新日時順|starttime:開始日時順)
start-min 最も古いイベントの開始日時(省略時:1970-01-01)
start-max 最も新しいイベントの終了日時(省略時:2031-01-01)
updated-min 最も古いエントリーの更新日時
updated-max 最も新しいエントリーの更新日時
author エントリーのauthor/name要素またはauthor/email要素(完全一致)
alt フィード種別(省略時:Atom 1.0|rss:RSS 2.0)

これらの検索パラメータのうち、「start-min」、「start-max」、「orderby」はカレンダーデータAPI特有のもので、それ以外はGData共通のものです。なお、カレンダーデータAPIは、GData共通の検索パラメータのうち、「category」はサポートしていません。

クラスライブラリを使ったサンプルプログラム

以下の例では、ClientLoginでトークンを取得した後、現在以降に開始予定のイベントエントリーのうち「横河」または「佐川」という文字列を含むものを、イベントの開始日時順に最大10件、という条件でフィードを取得しています。

<?php
$email = 'example@gmail.com';
$password = 'password';

require_once 'Google/Calendar.php';
$service = new Google_Calendar;

if (!$service->requestClientLogin($email, $password)) {
  exit("ClientLogin has failed.\n".$service->getResponseBody());
}
$queries = array(
  'q' => '横河|佐川',
  'max-results' => 10,
  'orderby' => 'starttime',
  'start-min' => date('Y-m-d\TH:i:s+09:00', time())
);
if (!$service->requestFeed($queries)) {
  exit("Requesting a feed has failed.\n".$service->getResponseBody());
}
header('Content-Type:application/xml;charset=UTF-8');
echo $service->getResponseBody();
?>

イベントの追加

イベントを追加するには、http://www.google.com/calendar/feeds/default/private/fullに対して以下のようなリクエストを送信します。

POST /calendar/feeds/default/private/full HTTP/1.1
Authorization: [GoogleLogin auth="ClientLogin_token"|AuthSub token="AuthSub_token"]
Content-Type: application/atom+xml

GData

GDataは、例えば以下のような、Atom 1.0に準じたフィードデータです。

<entry
    xmlns="http://www.w3.org/2005/Atom"
    xmlns:gd="http://schemas.google.com/g/2005">
  <category
    scheme="http://schemas.google.com/g/2005#kind"
    term="http://schemas.google.com/g/2005#event">
  </category>
  <title type="text">横河武蔵野FC vs. 佐川急便東京SC</title>
  <content type="text">最後の東京ダービー</content>
  <gd:where valueString="横河電機グラウンド">
  </gd:where>
  <gd:when
    startTime="2006-10-07T15:00:00+09:00"
    endTime="2006-10-07T17:00:00+09:00">
  </gd:when>
</entry>

イベントが「終日」の場合は、gd:when要素のstartTime/endTime属性値に日付のみを指定します。「繰り返し」のイベントの場合は、gd:recurrence要素を指定します。GData要素の詳細は、Common Elements: “Kinds”を参照してください。
GDataの文字エンコーディングはUTF-8です。日時は、Atom 1.0が採用しているRFC3339形式で指定します。

リクエストが成功した場合のレスポンスはHTTP 201 Createdで、そのボディは、送信したGDataにid要素やupdated要素などが追加されたフィードデータです。

クラスライブラリを使ったサンプルプログラム

以下の例では、ClientLoginでトークンを取得した後、イベントを追加しています。

<?php
$email = 'example@gmail.com';
$password = 'password';

$entry = <<<GDATA
<entry
    xmlns="http://www.w3.org/2005/Atom"
    xmlns:gd="http://schemas.google.com/g/2005">
  <category
    scheme="http://schemas.google.com/g/2005#kind"
    term="http://schemas.google.com/g/2005#event">
  </category>
  <title type="text">横河武蔵野FC vs. 佐川急便東京SC</title>
  <content type="text">最後の東京ダービー</content>
  <gd:where valueString="横河電機グラウンド">
  </gd:where>
  <gd:when
    startTime="2006-10-07T15:00:00+09:00"
    endTime="2006-10-07T17:00:00+09:00">
  </gd:when>
</entry>
GDATA;

require_once 'Google/Calendar.php';
$service = new Google_Calendar;

if (!$service->requestClientLogin($email, $password)) {
  exit("ClientLogin has failed.\n".$service->getResponseBody());
}
if (!$service->insert($entry)) {
  exit("Inserting an entry has failed.\n".$service->getResponseBody());
}
header('Content-Type:application/xml;charset=UTF-8');
echo $service->getResponseBody();
?>

イベントの変更と削除

イベントの変更及び削除は、そのイベントエントリーのlink要素中、rel属性値が「edit」のものの、href属性値に示されているURLに対してリクエストを送信します。

変更の場合は、以下のようにPUTメソッド(または、X-Http-Method-Overrideヘッダに「PUT」を指定したPOSTメソッド)を用いて、変更したGDataを送信します。

PUT Edit_URL HTTP/1.1
Authorization: [GoogleLogin auth="ClientLogin_token"|AuthSub token="AuthSub_token"]
Content-Type: application/atom+xml

GData

削除の場合は、以下のようにDELETEメソッド(またはX-Http-Method-Overrideヘッダに「DELET」を指定したPOSTメソッド)を用います。

DELETE Edit_URL HTTP/1.1
Authorization: [GoogleLogin auth="ClientLogin_token"|AuthSub token="AuthSub_token"]

いずれも、リクエストが成功した場合のレスポンスはHTTP 200 OKで、変更の場合は、そのボディは、送信したGDataが加工されたフィードデータです。

クラスライブラリを使ったサンプルプログラム

以下の例では、ClientLoginでトークンを取得した後、イベントの開始/終了日時を変更しています。

<?php
$email = 'example@gmail.com';
$password = 'password';

$edit_url = 'http://www.google.com/calendar/feeds/default/private/full/abcd/1234';
$entry = <<<GDATA
<entry
    xmlns="http://www.w3.org/2005/Atom"
    xmlns:gd="http://schemas.google.com/g/2005">
  <category
    scheme="http://schemas.google.com/g/2005#kind"
    term="http://schemas.google.com/g/2005#event">
  </category>
  <gd:when
    startTime="2006-10-07T14:00:00+09:00"
    endTime="2006-10-07T18:00:00+09:00">
  </gd:when>
</entry>
GDATA;

require_once 'Google/Calendar.php';
$service = new Google_Calendar;

if (!$service->requestClientLogin($email, $password)) {
  exit("ClientLogin has failed.\n".$service->getResponseBody());
}
if (!$service->update($entry, $edit_url)) {
  exit("Updating an entry has failed.\n".$service->getResponseBody());
}
header('Content-Type:application/xml;charset=UTF-8');
echo $service->getResponseBody();
?>

Some Tips

Category: ウェブ制作
Posted 2006年10月04日 22:53

2006年08月17日

HTMLを整形式のXML文書に修正するPHPクラス

ガーロ解任記念(無関係)。夏休みの宿題(嘘)。

HTMLなどにありがちなズサンなマークアップを、整形式のXMLドキュメントに補正するPHP用のクラスライブラリです。
一般的なXHTML変換処理にみられる、タグ書式の修正や不適切に用いられている解析対象記号の置換などに加えて、DTD的構成ルールを用いて、欠落している要素の補完や、要素の親子関係の補正をある程度行なうことができます。構成ルールは任意に指定可能で、これにより例えば、HTMLソースから不要な要素や属性を除去したり、別の要素に置換したりといったことも行えます。
各種のプログラミング言語において様々に提供されているマークアップの補正機能は、PHPでは、PECL拡張モジュール版のTidyなどを利用できますが、残念ながらこれらは、現在の一般的なレンタルサーバのPHP環境では必ずしも用意されているわけではありません。このスクリプトベースのクラスライブラリは、このようなバイナリ拡張モジュールが利用できない環境で、マークアップの補正を行う際の補助となることを意図したものです。

てゆうか、まあこいつのために作ったんだけど。

このクラスライブラリは、Free Software FoundationによるGNU Lesser General Public License(LGPL)のバージョン2.1あるいはそれ以降のバージョンの規約に従いライセンスされます。

応用例

» HTMLをXML化してDOMやXPathで操作するWebスクレイピング用PHPクラス

仕様とか制限事項

リリース

» 1.2.1 (stable) released 2007/05/17

※バージョン1.1からは、それ以前のバージョンと異なり、HTML_Sax3クラスのオブジェクトを別途に作成する必要がなくなりました。

ファイル名概要
HTMLParser.class.phpクラスライブラリ本体。
xhtml1-transitional_dtd.inc.phpXHTML 1.0 TransitionalのDTDを基に作成した、サンプルの構成ルールを表す連想配列を返す外部PHPファイル。
sample.phpHTMLを読み込み、補正結果を表示するサンプルアプリケーション。
ファイル中の、ターゲットのURIを指定している箇所を編集して、お好みのウェブページを表示してみてください。また、冒頭にあるふたつの変数を、output_xmlをTRUEに、output_xmlnsをFALSEにすると、Mozilla FirefoxなどではXMLドキュメントツリーを確認できると思います。

概要

このクラスライブラリは、SAXベースのXMLパーサである、PEARのXML_HTMLSax3用のイベントハンドラを定義したものです(従ってその利用にはXML_HTMLSax3が必要です)。XML_HTMLSax3は、PHPのネイティブXMLパーサと異なり、不適切なマークアップに対しても処理を継続します。XML_HTMLSax3は、一般的なSAXパーサと同様に、パース対象のドキュメント中に開始タグや終了タグ、キャラクターデータなどの要素を検出する都度、各要素に対応するハンドラを呼び出します。このクラスライブラリの各ハンドラメソッドは、XML_HTMLSax3から引き渡された各要素を、指定の構成ルールに従って文字列に構築していきます。

サンプルプログラム

以下のダメダメHTMLをパースする簡単なサンプルプログラムを見ていきます。
以下のHTMLはShift_JISエンコーディングで、sample.htmlという名称のファイルで存在しているものとします。

<META NAME=DESCRIPTION CONTENT=Foo&Bar>
<H1>
  <DIV ALIGN=CENTER>Foo&Bar</DIV>
</H1>
<HR NOSHADE>
</BODY>
<P>Foo<BR>Bar
<TABLE FOO=BAR>
<TD>Foo&Bar</TR></TR></TR></TR>
</TABLE>
<LI>Foo
<LI>Bar
<FOO>
<HTML>

まず、このHTMLを読み込み、UTF-8エンコーディングに変換しておきます(XML_HTMLSax3が扱える文字エンコーディングはUTF-8のみです)。

$source = file_get_contents('./sample.html');
mb_convert_variables('UTF-8', 'Shift_JIS', $source);

このクラスのオブジェクトを作成し、アーカイブに付属している、サンプルの構成ルールを表す連想配列を返す外部PHPファイル(xhtml1-transitional_dtd.inc.php)を指定します。このサンプルの構成ルールはXHTML 1.0 TransitionalのDTDを基に作成したものです。setRuleFileメソッドは外部ファイルを指定しますが、これに替えて、setRuleメソッドで配列を指定することもできます。

require_once('./HTMLParser.class.php');
$parser = new HTMLParser;
$parser->setRuleFile('./xhtml1-transitional_dtd.inc.php');

このクラスのオプションを指定します。