Amazonから大阪市立図書館の蔵書検索

無料って言葉に惹かれてうっかり「リーンの翼」を6話まとめて観てしまったbonlifeです。なんだろう、このすごい虚無感…。ということで、やる気を取り戻すためにいつもと違ったことをしてみました。1年(半)ぐらい前に流行ったAmazonから図書館の蔵書を検索するGreasemonkey。私もFirefoxを時折使うようになったので、大阪市立図書館の蔵書を検索するGreasemonkeyを書いてみました。id:amazonlibで公開されていたものをそのまま流用しています。

// ==UserScript==
// @name          Osaka City Library Lookup from Amazon book listings.
// @namespace     http://d.hatena.ne.jp/bonlife/
// @description   Osaka City Library Lookup from Amazon book listings.
// @include       http://*.amazon.*
// ==/UserScript==

var LIST_PAGE        = 0;
var DETAIL_PAGE      = 1;
var NO_SEARCH_PAGE   = 2;
var TARGET_URL       = "http://www.oml.city.osaka.jp/cgi-bin/internet/opi_103.cgi";
var CONDITION_URL    = "http://www.oml.city.osaka.jp/";

var BGCOLOR_IN_LIST  = "#FFE4B5"; //リスト画面での出力文字列の背景色

var SEARCH_LIST      = true;
var SEARCH_DETAIL    = true;
var SEARCH_EACHLIB   = true;

var SEARCH_00        = true;
var LIB_ID_00        = "   "; //全館
var LIB_NAME_00      = "";

var SEARCH_01        = true;
var LIB_ID_01        = "01"; // Osaka City
var LIB_NAME_01      = "\u5927\u962a\u5e02\u7acb";

main();

/**********************************************************************
メイン関数
**********************************************************************/
function main() {
  var isbn = "";
  var parent = null;

  switch(getPageType()){
  case LIST_PAGE:
      if(!SEARCH_LIST)return;
      for(var i=0;i<document.links.length;i++){
        isbn = getISBNfromLink(document.links[i]);
        if(isbn){
          parent = createParent();
          document.links[i].parentNode.insertBefore(parent, document.links[i]);
          if(SEARCH_EACHLIB){
               if(SEARCH_01) checkLibrary(isbn,parent,LIB_ID_01);
          }else{
               checkLibrary(isbn,parent,LIB_ID_00);
          }
        }
      }
      break;
  case DETAIL_PAGE:
      if(!SEARCH_DETAIL)return;
      var header = document.evaluate("//b[@class='sans']",
                      document,
                      null,
                      XPathResult.FIRST_ORDERED_NODE_TYPE,
                      null).singleNodeValue;
      isbn = getISBNfromURL();
      if(header){
        parent = createParent();
        header.parentNode.insertBefore(parent, header.nextSibling);
        if(SEARCH_EACHLIB){
             if(SEARCH_01) checkLibrary(isbn,parent,LIB_ID_01);
        }else{
             checkLibrary(isbn,parent,LIB_ID_00);
        }
      }
      break;
  case NO_SEARCH_PAGE:
      break;
  }
}
/**********************************************************************
ページの種別を判定
**********************************************************************/
function getPageType() {
  var href = document.location.href;
//2006.6.11 mod
//  var index = href.indexOf('ASIN');
//  var asin = href.substring(index+5,index+15);
  var index = href.indexOf('product');
  var asin = href.substring(index+8,index+18);
  if(isISBN(asin)){
    return DETAIL_PAGE;
  }

//カートのページ
//2006.6.11 mod
//  if(href.indexOf('cs_top_cart') > 0){
  if(href.indexOf('/cart/') > 0){
    return LIST_PAGE;
  }

  //hidden"url"のvalueに"books"が含まれればリストページ
  for(i=0;i<document.getElementsByName("url").length;i++){
    if (document.getElementsByName("url")[i].type == "hidden"){
      if (document.getElementsByName("url")[i].value.indexOf('books') > 0){
        return LIST_PAGE;
      }
    }
  }

  return NO_SEARCH_PAGE;
}
/**********************************************************************
リンクオブジェクトのURLからISBNを取得
**********************************************************************/
function getISBNfromLink(i_href) {
  var index = i_href.href.indexOf('ASIN');
  if(index==-1){return null;}
  var asin = i_href.href.substring(index+5,index+15);

  //ISBNかチェック。数字9桁 + 数字 Or X
  if(!isISBN(asin)){
    return null;
  }

  //テキストリンクのみ対象とする
  if(i_href.innerHTML.match(/.(jpg|gif)/i)){
    return null;
  }
  return asin;
}
/**********************************************************************
表示しているページのURLからISBNを取得
**********************************************************************/
function getISBNfromURL() {
  var href = document.location.href;
//2006.6.11 mod
//  var index = href.indexOf('ASIN');
//  var asin = href.substring(index+5,index+15);
  var index = href.indexOf('product');
  var asin = href.substring(index+8,index+18);
  if(!isISBN(asin)){
    return null;
  }
  return asin;
}
/**********************************************************************
ASINがISBNかチェック
**********************************************************************/
function isISBN(asin){
  if(asin.match(/(\d{9}[\d|X])/)){
    return true;
  }else{
    return false;
  }
}
/**********************************************************************
親オブジェクトを生成
**********************************************************************/
function createParent() {
  var parent = document.createElement('p');
  parent.style.backgroundColor = BGCOLOR_IN_LIST;
  return parent;
}
/**********************************************************************
蔵書のチェック(リストページ、詳細ページ兼用)
**********************************************************************/
function checkLibrary(isbn,parent,libId) {
  GM_xmlhttpRequest({
    method: "POST",
    url   : TARGET_URL,
    headers: {'User-Agent': 'Mozilla/4.0 (compatible) Greasemonkey',
              'Content-type': 'application/x-www-form-urlencoded'},
    data  : "KY_GNO=102"
            +"&KY_LANG=J"
            +"&KY_BUTTON=01"
            +"&HIS=101"
            +"&CT1=1"
            +"&ISBN=" + isbn
            +"&NUM=1"
            +"&MAX=1",
    onload:function(response){
        var spl_link = document.createElement('a');
        checkResult = response.responseText.match(/frameset/i);
        if ( checkResult ) {
            CONDITION_URL = CONDITION_URL
                            +"cgi-bin/internet/opi_103.cgi"
                            +"?KY_GNO=102"
                            +"&KY_LANG=J"
                            +"&KY_BUTTON=01"
                            +"&HIS=101"
                            +"&CT1=1"
                            +"&ISBN=" + isbn
                            +"&NUM=1"
                            +"&MAX=1";
        }
        spl_link.setAttribute('href', CONDITION_URL);
        spl_link.innerHTML= getResultString(getLibName(libId),checkResult);
        parent.parentNode.insertBefore(spl_link, parent.nextSibling);
    }
  });
}
/**********************************************************************
図書館名の取得
**********************************************************************/
function getLibName(libId) {
  switch(libId){
  case LIB_ID_01:return LIB_NAME_01;
  }
}
/**********************************************************************
文言の取得
**********************************************************************/
function getResultString(libName,bFound) {
  if(SEARCH_EACHLIB){
       if(bFound){
         return '<B>Found! ' + libName + '\u56f3\u66f8\u9928</B></BR>';
       }else{
         return 'Not Found. ' + libName + '\u56f3\u66f8\u9928</BR>';
       }
  }else{
       if(bFound){
         return '<B>Found! \u56f3\u66f8\u9928\u5168\u9928</B></BR>';
       }else{
         return 'Not Found. \u56f3\u66f8\u9928\u5168\u9928</BR>';
       }
  }
}

表示されるまでに5秒以上(6、7秒程度)かかるので実用的ではないような…。まぁ、気分転換ということで。
それはそうと、スーパーPRE記法ではアンパサンドが正しく表示されないものだと思ってましたが、そうでもないんですね。人力検索ではいっつも文字実体参照用の & a m p ; に置き換えられちゃうんですけど、あれはなぜなんでしょう。