2014年1月26日日曜日

Blogger API v3の使い方(ソースコードあり)

今回は全然ボドゲの話題ではないですのでご興味のある方だけどうぞ。

ずっとボドゲ会のレポを書いてきましたが、これだと特定のボドゲについてのレポだけを見ること
ができずちょっと気になってました。

Bloggerの機能ではラベルごとに一括で記事を表示することはできますが、複数のボドゲの
レビューを1つの記事に書いているのでボドゲごとのレポを表示することはできません。

最初は『1つのボドゲごとに1つの記事にしようか』とか考えてみましたがさすがに記事数が
増えて読みにくいし、何よりめんどくさいので却下しました。

というわけでそういう機能がないなら作ってしまえということで、コツコツ作り始めた次第です。

今回は、開発途中の『ボドゲレビュー検索(ベータ版)※一旦公開中止』の解説を中心に
進めていきたいと思います。



★概要

こんな仕様でつくります。
  • 『Blogger API v3』を使って特定のラベルの記事を取得する。
  • 取得した記事をから特定のボドゲの部分のみ抜き出して画面に表示する。
  • なんかうまく動かないのでIEは一旦対象外とする。

1.『Blogger API v3』の利用準備


googleが提供しているこのAPIは利用申請が必要です。
  1. google API console(https://cloud.google.com/console/project‎)にアクセスします。
  2. 左のメニューから【API と認証】 → 【API】 と進み【Blogger API v3】を有効にする際に使用目的やら聞かれますがたぶん空欄でOKです。
    ※プロジェクトを作っていない方は適当に作りましょう。
  3. 5営業日以内に承認メールが来てステータスが『有効』になればOKです。

こんな感じになればOK(Task APIは無関係です)


2.ブログIDの取得


Bloggerの管理画面のURLからブログIDを取得します。『blogID=**********』の数字部分です。
参考:https://support.google.com/blogger/answer/42191?hl=ja


3.アクセス制限設定とAPIキーの取得


google API consoleの認証情報からアクセス制限を設定します。
このブログからのアクセスのみ許可すればOKなので、【新しいキーを作成】→【ブラウザキー】を選択して、許可対象リファラーに【http://boardge.blogspot.jp/*】と設定します。これでAPIキーが生成されますのでこのキーを使用してAPIにアクセスします。

※動作確認等のためには、【サーバーキー】を選択して自分のIPアドレスを設定すればOKです。
 反映にちょっと時間がかかるので注意。


4.動作確認


ここまでできたら動作確認をしてみましょう。
取得したブログIDとAPIキーを使用して下記のURLをブラウザで表示します。
【https://www.googleapis.com/blogger/v3/blogs/[ブログID]/posts?key=[API key]】

こんな感じになれば成功です。

下記の表示が出てきたらアクセス制限でブロックされているので設定を見なおしてください。

{
 "error": {
  "errors": [
   {
    "domain": "usageLimits",
    "reason": "accessNotConfigured",
    "message": "Access Not Configured. Please use Google Developers Console to activate the API for your project."
   }
  ],
  "code": 403,
  "message": "Access Not Configured. Please use Google Developers Console to activate the API for your project."
 }
}

さらにgoogle API consoleではAPIのシミュレーションが出来ますのでいろいろ試してみると
わかりやすいです。
【APIと認証】→【API】→【Blogger API v3】から試せます。


5.javascriptで記事を取得するコード


長くなりましたがいよいよコーディングです。
と言ってもjQueryのajax関数を利用してAPIへアクセスするだけのシンプルなものです。
下記の【data】という変数に先ほどブラウザで確認したデータが入っていますので、成功の場合は
記事を画面表示する【dataSet】関数を呼び出し、失敗の場合はエラー内容(data.statusText)を
ポップアップ表示します。

また、dataの中にnextPageTokenが設定されている場合は記事数が多く、複数回通信しないと
すべての記事を取得できていないため、apiURLにnextPageTokenを設定して記事取得関数を
再帰呼び出しします。


/**********************
 * 定数・変数の定義
 **********************/

//blogger api v3 のURL
var BASE_URL = "https://www.googleapis.com/blogger/v3/blogs/";

//blogID
var BLOG_ID = [blog ID];

//API key
var API_KEY = [API key];

/***********************
 * ブログ記事を取得する
 ***********************/
function postGet(){
    var apiUrl = BASE_URL + BLOG_ID + "/posts?labels=" + encodeURI(srcWord) + "&key=" + API_KEY;

    jQuery.support.cors = true;
    
    //nextPageTokenが存在する場合はURLパラメータに追加する
    if(typeof nextPageToken !== "undefined"){
        apiUrl += "&pageToken=" + nextPageToken;
    }

    //blogger API を実行する
    $.ajax({
        type: "GET",
        url: apiUrl,
        cache:false,
        data:null,
        scriptCharset: 'utf-8',
        success: function(data){
            //成功の場合は画面表示処理を行う
            dataSet(data);

            if(typeof data.nextPageToken !== "undefined"){
                //data.nextPageToken が存在する場合(=取得記事が複数ページに渡る場合)
                //記事取得処理を再帰呼び出しする。
                nextPageToken = data.nextPageToken;
                postGet();
            }
        },
        error: function(data){
            //エラーの場合
            alert(data.statusText);
        }
    });
}

6.その他コードをまとめて完成


ほか関数はダイジェストでお送りします。

◆$(document).ready()
ページの読み込みが完了したタイミングで実行されます。
URLが【http://boardge.blogspot.jp/p/ver.html?q=XXXXX】だった場合XXXXXの値を取得して
このラベル名を含む記事を取得して画面表示します。


◆function getParm()
URLパラメータを取得します。


◆function search()
検索ボタンを押下した際の処理です。テキストエリアに入力された文字列をURLパラメータに
設定して画面を再読み込みします。すると【$(document).ready()】が実行されます。


◆function dataSet(data)
取得したブログ記事から検索したボドゲ名の部分だけ切り出して画面表示します。
記事はボドゲごとに【<h4>】タグで区切って書いているので(昔のは適当かも)それをキーに
分割して検索ワードを含む場合のみ画面に表示します。


まとめるとこんな感じです。

<html>
    <head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8">
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script type="text/javascript">
        //<![CDATA[
        <!--
            
            /**********************
             * 定数・変数の定義
             **********************/
            
            //blogger api v3 のURL
            var BASE_URL = "https://www.googleapis.com/blogger/v3/blogs/";

            //blogID
            var BLOG_ID = [blog ID];

            //API key
            var API_KEY = [API key];
            
            //検索文字列
            var srcWord;

            var nextPageToken;
            
            //現在ページのURL
            var url;
            
            
            
            /**********************************
             * DOM構築完了後実行されるメイン処理
             **********************************/
            $(document).ready(function(){
                
                //URLパラメータの取得
                var params = getParm();

                //URLパラメータ[q]に検索ワードが設定されている場合
                if(typeof params["q"] !== "undefined" && params["q"] != ""){

                    //URLパラメータから検索ワードを取得する
                    srcWord = decodeURI(params["q"]);
                    
                    //検索テキストボックスに検索ワードを設定する
                    document.getElementById("searchTextBox").value = srcWord;
                    
                    //表示中の記事を消す
                    $("#result").empty();

                    //blogger APIを使用してブログ記事を取得する
                    getPost();
                }
            });

            /****************************
             * URLパラメータを取得する関数
             ****************************/
            function getParm(){
                //return するパラメータと値の配列
                var params = [];
                
                //処理用一時変数
                var hashes; 
                var hash; 
        
                //現在ページのURLを取得する
                url = location.href;
                
                //URLをパラメータごとに分割する
                hashes = url.slice(url.indexOf('?') + 1).split('&'); 

                //パラメータごとに配列に格納する
                for(var i = 0; i < hashes.length; i++) { 
                    hash = hashes[i].split('='); 
                    params.push(hash[0]); 
                    params[hash[0]] = hash[1]; 
                }
                return params
            }

            /***********************
             * 検索ボタン押下時の処理
             ***********************/
            function search(){
                //パラメータを除いたURLの文字数
                var urlEndIndex;
                
                //パラメータを除いたURLを取得する
                url = location.href;
                urlEndIndex = url.indexOf("?");
                if(urlEndIndex != -1){
                    url = url.substring(0,urlEndIndex);
                }

                //テキストボックスの内容をURLパラメータに設定して画面遷移する
                srcWord = document.getElementById("searchTextBox").value;
                if(srcWord != ""){
                    url += "?q=" + encodeURI(srcWord);
                    location.href = url;
                }
                return false;
            }
            
            /***********************
             * ブログ記事を取得する
             ***********************/
            function getPost(){
                var apiUrl = BASE_URL + BLOG_ID + "/posts?labels=" + encodeURI(srcWord) + "&key=" + API_KEY;

                jQuery.support.cors = true;
                
                //nextPageTokenが存在する場合はURLパラメータに追加する
                if(typeof nextPageToken !== "undefined"){
                    apiUrl += "&pageToken=" + nextPageToken;
                }
        
                //blogger API を実行する
                $.ajax({
                    type: "GET",
                    url: apiUrl,
                    cache:false,
                    data:null,
                    scriptCharset: 'utf-8',
                    success: function(data){
                        //成功の場合は画面表示処理を行う
                        dataSet(data);

                        if(typeof data.nextPageToken !== "undefined"){
                            //data.nextPageToken が存在する場合(=取得記事が複数ページに渡る場合)
                            //記事取得処理を再帰呼び出しする。
                            nextPageToken = data.nextPageToken;
                            getPost();
                        }
                    },
                    error: function(data){
                        //エラーの場合
                        alert(data.statusText);
                    }
                });
            }
            
            /********************************
             * ブログ記事を編集して画面に表示する
             ********************************/
            function dataSet(data){
                //ブログ記事の文字列
                var str;
                
                //ブログ記事の中の各ボドゲレビューの文字列
                var postStr;
                
                //ブログ記事の中の各ボドゲレビューの数
                var postCount;
                var postCountList;
        
                var tmpIndex = 0;
                var strIndex = 0;
                
                for (var i = 0; i < data.items.length; i++){

                    //ブログ記事のタイトルを表示する
                    $("#result").append("<h1>" + data.items[i].title + "</h1>");
                    
                    //ブログ記事・ボドゲレビュー数の取得
                    str = data.items[i].content;
                    postCount = 0;
                    var postCountList = str.match(/<h4>/g);
                    if(postCountList != null){
                        postCount = postCountList.length;
                    }

                    //検索大賞のボドゲレビュー部分のみ抜き出して画面に表示する
                    //※ボドゲレビューは【<h4>】タグで区切って書いているのでそれをキーに分割する。
                    //※<h4>タグの中に検索ワードを含んでいる場合のみ表示する。
                    strIndex = 0;
                    for(var j = 0; j <= postCount; j++){
                        tmpIndex = strIndex;
                        strIndex = str.indexOf("<h4>", strIndex + 4);
                        if(strIndex == -1){
                            strIndex = str.length;
                        }
                        postStr = str.substring(tmpIndex, strIndex)
                        if(postStr.indexOf(srcWord) != -1){
                            if(postStr.indexOf(srcWord) < postStr.indexOf("</h4>")){
                                postStr = "<h4>" + postStr.substring(postStr.indexOf(".") + 1, postStr.length);
                                $("#result").append(postStr);
                            }
                        }
                    }
                }
            }
            //-->
            //]]>
        </script>
    </head>
    <body>
        <form method="get" name="searchForm" action="search()">
            <input type="text" value="" name="searchTextBox" id="searchTextBox">
            <input type="button" value="検索" onclick="search();" /><br>
        </form>
        <div id="result"></div>
    </body>
</html>


7.動作イメージ

ボーナンザの記事だけが表示されます。
例:http://boardge.blogspot.jp/p/ver.html?q=ボーナンザ



8.課題


よくわからないんですがIEで動作しません。
いろいろぐぐってみると、jQueryのajax関数がうまく動いて無いようで[object error]なるものが
発生します。
エラー画面

さらにIEでは、XDomainRequest を使うべしとか「jQuery.support.cors = true;」を書くといいとか
キャッシュが悪さしてるとか、いろいろ試してみましたが一向に改善しないので一旦無視します。

ちなみにこのブログへのアクセスの15%がIEです。(15%の皆さんごめんなさい!)



★今後やりたいこと

◎ラベルの一覧表示からボドゲ記事一覧へのリンク
これはIEを無視すれば技術的にはそんなに難しくないはず。


◎ボドゲ会カレンダの簡易登録
これはぜひやりたい。現在は、htmlを直接編集しているが実はすごい手間がかかる。
自動でhtml コードを生成してくれるツールをエクセルで作ったり工夫はしているのですがいかんせんボドゲ会で遊ぶのに忙しくて時間が取れない
構想としては、google docsあたりにボドゲ会スケジュールを登録しておいてページ側から自動で読み込んで画面表示させようかと思います。
(完成したらもう少し更新頻度をあげられるかも?)


◎Twitter BOT作成
上記と同じく、実はTwitter BOTへの登録も手作業なんですごい面倒!
google docsに登録したスケジュールを自動でつぶやいてくれればいいんですが。
ただそういう機能は今使ってるフリーのBOT作成ツールにはないので、そこから作る必要がありすごい大変。これは保留です。



0 件のコメント:

コメントを投稿