Bloggerのサイトマップ(投稿一覧)を自動作成する

完成品

サイトマップ

サイトマップ

作成目的

ブログ全体に存在する記事/ページを簡易表示したい。ブログの全体を簡易に閲覧可能なページを作成したい。ただし、記事作成毎に更新するのは大変なため、自動更新としたい。

作成経緯

  1. Blogger 記事 を 一覧 表示 する ページ の 作成方法 - galifeで作成する
    • 目的のものが作成出来るが、以下の点が不満
    • JavaScriptの使用が必須
    • 一覧表示までに遅延(数秒)が発生する
  2. 不満点解消のため、自作する

動作概要

  1. 投稿一覧をスプレットシートに記入する
  2. ページ一覧をスプレットシートに記入する
  3. 投稿一覧とページ一覧を使用してBloggerページを更新する
    1. 投稿一覧、ページ一覧からサイトマップページのHTMLを作成する
    2. OAuth2のアクセストークンを取得する
    3. BloggerAPIでページを更新する
  • GoogleAppsScriptのトリガー設定でスクリプトを自動実行する

ソースコード

sitemap.gsvar blogid = '{BloggerID}';
var pageid = '{ページID}';
var sheetid = '{スプレッドシートID}';

// アクセストークンの取得
function fetchAccessToken() {
  var client_id = '{クライアントID}';
  var client_secret = '{クライアント シークレット}';
  var redirect_uri = '{リダイレクトURI}';
  var refresh_token = '{リフレッシュトークン}';

  var res = UrlFetchApp.fetch("https://www.googleapis.com/oauth2/v4/token", {
    "method" : "POST",
    payload : {
      "refresh_token" : refresh_token,
      "client_id" : client_id,
      "client_secret" : client_secret,
      "redirect_uri" : redirect_uri,
      "grant_type" : "refresh_token"
    }
    //, muteHttpExceptions : true
  });

  return JSON.parse(res.getContentText());
}

function createBlogContent(sheetApp, type) {
  var isPost = (type == 'posts');
  var sheet = sheetApp.getSheetByName(type);
  var row = sheet.getLastRow();
  if (row <= 1) {
    return ''; 
  }

  // A1=(1, 1)
  var values = sheet.getRange(2, 1, row-1, 4).getValues();
  var list = [];
  for (var i=0; i<values.length; i++) {
    list.push({url:values[i][0],update:values[i][1],posted:values[i][2],title:values[i][3]});
  }

  // タイトルで並び替え
  list.sort(function(a, b) {
    var at = a.title.toUpperCase();
    var bt = b.title.toUpperCase();
    if (at < bt) { return -1; }
    if (at > bt) { return 1; }
    return 0;
  });

  // HTML作成
  var lines = [];
  var now = Date.now();
  lines.push('<ul class="poststoc-list">');
  list.forEach(function(v, i, a) {
    var url = v.url;
    if (url.indexOf('https:') == 0) {  url = url.substr(6); }
    if (url.indexOf('http:') == 0) {   url = url.substr(5); }
    var posted = new Date(v.posted).getTime();

    lines.push('<li class="poststoc-item">');
    lines.push('<span class="poststoc-title"><a href="'+url+'">'+v.title+'</a></span>');
    // 28日以内
    if (isPost && now < posted + 28*24*60*60*1000) {
      lines.push('<span class="poststoc-new">NEW !</span>');
    }
    lines.push('</li>');
  });
  lines.push('</ul>');

  return lines.join('');
}

function createLabelContent(sheetApp) {
  var sheet = sheetApp.getSheetByName('posts');
  var row = sheet.getLastRow();
  if (row <= 1) {
    return ''; 
  }

  // ラベル取得
  var values = sheet.getRange(2, 5, row-1, 1).getValues();
  var list = [];
  for (var i=0; i<values.length; i++) {
    var array = values[i][0].split(',');
    for (var k=0; k<array.length; k++) {
      if (list.indexOf(array[k]) == -1) {
        list.push(array[k]);
      }
    }
  }
  list.sort(function(a, b) {
    var at = a.toUpperCase();
    var bt = b.toUpperCase();
    if (at < bt) { return -1; }
    if (at > bt) { return 1; }
    return 0;
  });

  // HTML作成
  var lines = [];
  lines.push('<ul class="poststoc-list label">');
  list.forEach(function(v, i, a) {
    if (v != '') {
      lines.push('<li class="poststoc-item">');
      lines.push('<span class="poststoc-title"><a href="//www.bugbugnow.net/search/label/'+v+'">'+v+'('+obj[v]+')</a></span>');
      lines.push('</li>');
    }
  });
  lines.push('</ul>');

  return lines.join('');
}

// コンテンツ作成
function createSitemapContent() {
  // スプレットシートの取得
  var sheetApp = SpreadsheetApp.openById(sheetid);

  // HTML作成
  var lines = [];
  var now = Date.now();
  lines.push('<style>.poststoc-new{margin-left:1em;font-size:small;color:#F08300;}.poststoc-list.label{list-style-type: none;}.poststoc-list.label li{display: inline-block;padding-right: 0.75em;}</style>');
  lines.push('<div class="poststoc">');
  /*
  lines.push('<ul class="poststoc-list">');
  lines.push('<li class="poststoc-item">');
  lines.push('<span class="poststoc-title"><a href="//www.bugbugnow.net/">TOP</a></span>');
  lines.push('</li>');
  lines.push('</ul>');
  */
  lines.push('<h4>投稿</h4>');
  lines.push(createBlogContent(sheetApp, 'posts'));
  lines.push('<h4>ページ</h4>');
  lines.push(createBlogContent(sheetApp, 'pages'));
  lines.push('<h4>ラベル</h4>');
  lines.push(createLabelContent(sheetApp));
  lines.push('</div>');

  return lines.join('');
}

// サイトマップ更新
function updateSitemap() {
  var credentials = fetchAccessToken();

  //リクエストの設定 
  var json = {
    title: 'サイトマップ',
    content: createSitemapContent()
  };
  var fetchOptions = {
    method:"PUT",
    contentType:"application/json",
    headers:{
      Authorization:"Bearer "+credentials.access_token,
    }, 
    payload:JSON.stringify(json)
  };

  //リクエストURLを作成  
  var url = 'https://www.googleapis.com/blogger/v3/blogs/'+blogid+'/pages/'+pageid;

  //fetch
  var res = UrlFetchApp.fetch(url, fetchOptions);

  // ログ
  Logger.log(url);
  Logger.log(res.getContentText());
}

function sitemap() {
  // 投稿一覧更新
  updatePost();

  // ページ一覧更新
  updatePage();

  // サイトマップを更新する
  updateSitemap();
}

更新履歴

更新日内容
2018/07/01初版
2018/08/12[fix] ラベルなし対応

補足

上記ソースコードを使用する場合、下記の値を変更する必要があります。

  • {BloggerID}
    • 管理ページのURLから取得可能です
  • {ページID}
    • 管理ページのURLから取得可能です
  • {スプレッドシートID}
    • スプレットシートのURLから取得可能です
  • {クライアントID}
  • {クライアント シークレット}
  • {リダイレクトURI}
  • {リフレッシュトークン}
    • GoogleAPIsから取得可能です
    • BloggerのGoogleアカウントで取得する必要があります
    • リフレッシュトークンは、別途認証を行い取得する必要があります
  • www.bugbugnow.net
    • 使用するサイトのドメイン

参考