2009年9月20日日曜日

JavaScript で外部サイトのフィードを取得 - Google AJAX Feed API を利用

0. 目次

  1. 公式 HP と、外部ブログを組み合わせる事例が多い
  2. ユーザが HP を更新したいなら、CMS を利用するのがいい
  3. 外部ブログのフィードを取得する方法
  4. Google AJAX API
  5. Google AJAX Feed API
  6. フィードを取得するためのソースコード

 

1. 公式 HP と、外部ブログを組み合わせる事例が多い

見かけよりも、サイトの内容が大事

090918-006企業サイトを見ていると、公式 HP とは別に、ブログは外部サービスを利用している例を見かける。

「金かけてるのに、自社の紹介とブログが別って、何で?」

CMS を使って、一体感のあるデザインにした方がいいと思うけれど、別々の例をいくつか見ていたら、次第に慣れてきた。

「それでも、まぁいいかぁ~」

という気になる。

Flash を中心に構築しているサイトであれば、ブログを組み込むのは手間がかかるだろう。

Twitter の興隆っぷりを見ていると、発信する媒体の統一性など、受信してくれる価値に比べたら微々たるもの。情報が文脈から切り離され、情報としてのみ価値を有し、デザインは二の次というのは潔い。RSS フィードを受信することが習慣化するにつれ、ますますそういう意識に陥る。見かけよりも中身が大事。

 

されど見かけも重要

しかし、デザインに全体的な統一感があると、サイトをはじめて見る人にしてみれば、内容の理解がしやすい。まとまって見える分記憶に残りやすい。

「ちゃんとしたところだな」

という感じを演出できる。

以前、それほど PC に詳しくない人から、

「ブログはホームページではないですよね?」

と言われたことがある。最初、その意味がわからなかった。できあいのシステムを利用したブログと、所謂 HP 作成用のソフトで作ったものとは、別のものだと認識しているようだ。感覚として、そこに境界を感じるらしい。

そういう認識を持つ人にとって、ブログでサイトを作って

「これホームページにしておきませんか?」

というのは通じない。体面が大事な企業の HP が、Blogger で何のカスタマイズもされていなかったら、

「ここ大丈夫か?」

と思ってしまう感覚は理解できる。人は、見かけで判断することもあると。

追記 (2010.5.3) : ダダ漏れ東京女子流 のインタビューが配信されているのを見た。そのグループのサイトを見たら、一目で色々なサービスを利用していることがわかるデザインだった。公式サイトは看板で、個別の内容は各々のサービスを参照という形が、最近益々不自然でない印象を受ける。

 

2. ユーザが HP を更新したいなら、CMS を利用するのがいい

年間少しのお金を出せば、ドメインとサーバスペースを取得できる。できあいのブログが嫌なら、CMS をインストールすれば、快適な環境が得らえる。

しかし、既にプロバイダーと契約していると、容量が少ないながら無料でホームページ用のスペースを持っていることが多い。既にその場に HP を作っている場合、

「アドレスを変えたくないし、無料のスペースがあるのでこれ以上はお金をかけたくない。」

と考える人がいる。そういう心情も理解できなくはない。しかし、加えて

「でも、ブログのように自分で HP の内容を更新したい。」

となると、ちょっと悩んでしまう。 (+_+) 

サーバの仕様によっては CGI が使えるので、CMS で置き換えることもできる。しかし、無料の場合、制約が多くて無理なことも多い。もし、Perl が使えるなら、シンプルで軽そうな blosxom は良さげ。これでも処理が重たく感じられるようなサーバなら、別の手を考えるしかない。

 

3. 外部ブログのフィードを取得する方法

HP にとって、アドレスは昔ほど意味を持たなくなっている。単語を検索して、辿りつくのが普通。

であるなら、既存の HP は潰して、別の URL へリダイレクトさせるだけの役割を持たせればいい。しかし、今あるものは、それはそれで活用したい場合、どうしよう?

苦肉の策として、日々更新したい内容は、外部のブログで更新。そのフィードを、スクレイピングして既存のホームページに表示する。

しかし、制約があって、サーバに RSS を取得するライブラリをインストールして利用する方法がわからなかったら、どうしよう?

JavaScript: 外部RSS読み込み by “Google Ajax Feeds API” | t.p.fields - web+tech info  によると、

とあるお仕事でJavaScriptを用いて外部RSSを読み込む作業をすることになった。…

JavaScriptでのクロスドメイン処理は難しいので、”Google Ajax Feeds API“を使わせてもらうことにした。

“Google Ajax Feeds API”は、かの有名なGoogleが提供してくれる、RSS/Atomなどの各種フィードを取得するためのJavaScript API。

なるほど、これを使って外部ブログのフィードを読み込み HP に表示すればいい訳か。

そこで、以下の図のような構成を想定。

  1. HP には、トップページに日々更新した内容を表示。
  2. レイアウトは 2段組で、左側にはブログのタイトルの一覧を表示し、右側にはその内容を配置。
  3. ブログの内容は、外部ブログからまるごと取得して表示。
  4. 内容の右下の「前の投稿」のリンクは、トップページに表示した記事よりも前の投稿へのリンク。このリンクにより外部ブログへ飛ぶ。

090918-005

これで、既存の HP はそのままで、ブログを使ってユーザが更新できる。ブラウザの JavaScript が有効であれば、見かけそのままで、HP のアドレスを変更する必要もない。かなり強引な方法だけれど。。 ^^;

 

4. Google AJAX API

Home Page - Google AJAX API によると、

Google AJAX API では、リッチでダイナミックなウェブサイトを JavaScript と HTML だけで実装できます。JavaScript コードを数行追加するだけで、自分のサイトに地図動的な検索ボックスを追加したり、フィードをダウンロードできます。

この中に Google AJAX Feed API がある。

デベロッパー ガイド - Google AJAX API もチェック。共通の枠組みについて説明がされている。

 

5. Google AJAX Feed API

Google AJAX Feed API によると、

AJAX Feed API を使用すると、JavaScript だけを使用して、公開されている Atom フィードや RSS フィードをダウンロードできるので、自分のコンテンツや Google Maps API などの他の API に、フィードを簡単にマッシュアップできます。

早速利用方法から見ていく。

AJAX API キーの使用 によると、

この API を使用するためにキーは必要ありません。アプリケーションやサイトでキーを使用するかどうかは、完全に任意です

今回は AJAX API キーは利用しない。

この API が扱いやすそうなのは、JSON 形式と XML 形式の結果 によると、

AJAX Feed API の JSON 形式は、オリジナルのフィードを簡素化し、標準化したものです。これにより Atom および RSS の titledescriptionsummary などの属性が共通の JSON プロパティにマッピングされるので、Atom フィードにも RSS フィードも統一的に利用できます。

必要な情報は JSON 形式の結果 を見ればよい。

予め承知しておくことは、

AJAX Feed API は Feedfetcher を使用しているため、API から提供されるフィードのデータが最新ではない場合もあります。Google のフィード クローラ (Feedfetcher) がフィードを取得する頻度は、ほとんどのサイトでは 1 時間に 1 回を超えることはありません。一部の頻繁にアップデートされるサイトに対しては、更新の頻度が高くなります。

(フィードのクロール頻度 より)

外部ブログの投稿がリアルタイムに HP の方に更新されることはない。pubsubhubbub でリアルタイムになるといいのだけれどなぁ。

 

6. フィードを取得するためのソースコード

表示

index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <head>
  <link rel="stylesheet" type="text/css" href ="gajax.css"></link>
  <script type="text/javascript" src="blog.js"></script>
  <script type="text/javascript" src="http://www.google.com/jsapi"></script>
  <script type="text/javascript" src="gajax.js"></script>
 </head>
 <body>
  <!-- フィードのタイトル一覧 -->
  <div id="feedTitle"></div>
  
  <div id="main">
   <!-- フィードの内容 -->
   <div id="feed"></div>
   <!--前の投稿へのリンク -->
   <div id="nextLink"></div>
  </div>
 </body>
</html>

 

gajax.css

基礎編1 フロートを使ってみる を参考に CSS で段組。

#feedTitle { 
 font-size: small;
 float:left;
 padding: 10px;
 width: 30%;
}
#main {
 float:left;
 width: 67%;
}
#nextLink {
 text-align: right;
}
.title { font-size: x-large; }
.content { padding: 20px; }

 

Google AJAX Feed API の利用

gajax.js

はじめての Google AJAX Feed API を元に作成。先ほどのデベロッパー ガイド - Google AJAX API も参考に。

以下のコードは、冒頭の blog オブジェクトで、対象の外部ブログのフィード URL と、1 ページに表示する投稿数、外部ブログの URL を指定。

その他以下を参照。

ブログのエントリーの公開日時の扱いは、Date オブジェクトを生成してから使うことに気をつける。

publishedDate

エントリが公開された日付の、「13 Apr 2007 12:40:07 -0700」という形式の文字列。new Date(entry.date) を使って日付を解析することができます。

(JSON 形式の結果 より)

var blog = { 
 feedurl: "http://jutememo.blogspot.com/feeds/posts/default",
 numOfPost: 7, // 1 ページに表示する投稿数
 url: "http://jutememo.blogspot.com/"
}

// エントリーのタイトルをリンク付きにして div 要素に入れて返す。
function createTitleWithLink(entry, idx){
 var div = document.createElement("div");
 var anchor = document.createElement("a");
 anchor.href = entry.link;
 anchor.setAttribute("name", idx);
 anchor.appendChild(document.createTextNode(entry.title));
 div.className = "title";
 div.appendChild(anchor);
 return div;
}
// エントリーの内容を div 要素に入れて返す。
function createContent(entry){
 var div = document.createElement("div");
 div.innerHTML = entry.content;
 div.className = "content";
 return div;
}
// エントリーのタイトルへのアンカーを作成し、div 要素に入れて返す。
function createFeedTitle(entry, idx){
 var anchor = document.createElement("a");
 var div = document.createElement("div");
 anchor.setAttribute("href", 
  location.protocol + "//" + location.hostname + location.pathname + "#" + idx);
 anchor.appendChild(document.createTextNode(entry.title));
 div.appendChild(anchor);
 return div;
}
// 「前の投稿」を生成
// createURL 関数は blog.js で定義。
function createNextLink(entry){
 var anchor = document.createElement("a");
 anchor.href = createURL(blog.url, blog.numOfPost, new Date(entry.publishedDate));
 anchor.appendChild(document.createTextNode("前の投稿"));
 return anchor;
}
google.load("feeds", "1");
function initialize() {
 var feed = new google.feeds.Feed(blog.feedurl);
 feed.setNumEntries(blog.numOfPost)
 feed.load(function(result) {
  if (!result.error) {
   var divFeedTitle = document.getElementById("feedTitle");
   var divFeed = document.getElementById("feed");
   var divNextLink = document.getElementById("nextLink");
   for (var i = 0; i < result.feed.entries.length; i++) {
    var entry = result.feed.entries[i];
    // フィードのタイトル一覧を生成
    divFeedTitle.appendChild(createFeedTitle(entry, i));
    // フィードのタイトルを生成
    divFeed.appendChild(createTitleWithLink(entry, i));
    // フィードの内容を生成
    divFeed.appendChild(createContent(entry));
   }
   // 前の投稿へのリンクを生成
   divNextLink.appendChild(createNextLink(result.feed.entries[blog.numOfPost-1]));
  }
  });
}
google.setOnLoadCallback(initialize);

 

Blogger における「前の投稿」へのリンク

blog.js

JavaScript で Blogger の「前の投稿」へのリンクを生成 を参照。

// Blogger の場合

// num が一桁の数字のとき先頭に 0 を加えて二桁にする
function padzero(num){
 return ("" + num).length == 1 ? "0" + num : num;
}
function format(date){
 return date.getFullYear() + "-" + 
  padzero(date.getMonth()+1) + "-" + 
  padzero(date.getDate()) +
  encodeURIComponent("T" + padzero(date.getHours()) + ":" + 
   date.getMinutes() + ":" + 
   padzero(date.getSeconds()) + "+") +
  "09:00";
}
function createURL(url, numOfPost, date){
 return url + "search?updated-max=" + format(date) + "&max-results=" + numOfPost;
}