いろんなサイトから ISBN を抽出するグリモンのテスト

2008/05/22 追記
via id:hetappiさんのISBNのグリモンを少し弄った。 - tyoro.exe
tyoro さんに指摘してもらって wedata から json 形式で取得したときの Content-Type を text/javascript から application/json に修正しました。ありがとです。
このまえ少し止まってた時に変わったのかな…。



wedata にとりあえず作って1か月ほど放置していた Item - データベース: ISBN Scraper - wedata 。CrossBookSearch のソース中にだーっと書いてあった ISBN を抽出する XPathWiki で管理することで、編集を容易にし他のスクリプトなどでも使えるようになります。きっと。


とりあえずテストグリモンでは Document もしくは http な URL 文字列から ISBN をゲットできるようにした。

var sc = new ISBNScraper;
sc.scrape(document, function(isbn) {
  if (isbn)
    console.dir(isbn);
});
sc.scrape('http://www.oreilly.co.jp/books/9784873113654/', function(isbn) {
  if (isbn)
    console.dir(isbn);
});
isbn10 "4873113636"
isbn13 "9784873113630"

isbn10 ""
isbn13 "9784873113654"

まだ wedata には Amazonオライリーしかないけど、http://coderepos.org/share/browser/lang/javascript/userscripts/crossbooksearch.user.js? にいっぱいあるので誰か(ry。

// ==UserScript==
// @name           TestISBNScraper
// @namespace      http://d.hatena.ne.jp/hetappi
// @include        *
// ==/UserScript==

var sc = new ISBNScraper;
sc.scrape(document, function(isbn) {
  if (isbn)
    console.dir(isbn);
});
sc.scrape('http://www.oreilly.co.jp/books/9784873113654/', function(isbn) {
  if (isbn)
    console.dir(isbn);
});

function ISBNScraper() {
  return {
    _getSiteInfo: function(callback) {
      var cache = eval(GM_getValue('cache'));
      if (cache && cache.expire > new Date().getTime()) {
        callback(cache.siteinfo);

      } else {
        new GMUtil().get('http://wedata.net/databases/isbn%20scraper/items.json', function(resp) {
          if (resp && resp.response.body.type == 'json') {
            var items = resp.response.body.json;
            var cacheNew = {
              siteinfo: items,
              expire: new Date(new Date().getTime() + 24 * 60 * 60 * 1000)
            };
            GM_setValue('cache', uneval(cacheNew));
            callback(items);
          } else {
            callback(cache ? cache.siteinfo : null);
          }
        });
      }
    },

    _getIsbn: function(doc, url, items) {
      for (var i = 0, m = items.length; i < m; ++i) {
        if (!new RegExp(items[i].data.url).test(url))
          continue;
        var isbn = {};
        if (items[i].data.isbn10)
          isbn.isbn10 = doc.evaluate(
            items[i].data.isbn10, doc, null, XPathResult.STRING_TYPE, null).stringValue;
        if (items[i].data.isbn13)
          isbn.isbn13 = doc.evaluate(
            items[i].data.isbn13, doc, null, XPathResult.STRING_TYPE, null).stringValue;
        return isbn;
      }
      return null;
    },

    scrape: function(src, callback) {
      if (!src) {
        callback(null);
        return;
      }

      var self = this;
      self._getSiteInfo(function(items) {
        if (!items) {
          callback(null);
        } else if (typeof src == 'string' && new RegExp('^https?://').test(src)) {
          new GMUtil().get(src, function(resp) {
            callback(
              resp ? self._getIsbn(resp.response.body.document, src, items) : null);
          });
        } else if (typeof src == 'object' && new GMUtil().instanceof(src, Document)) {
          callback(self._getIsbn(src, src.location.href, items));
        } else {
          callback(null);
        }
      });
    }
  };
}
function GMUtil() {
  return arguments.callee._self || (arguments.callee._self = {

    instanceof: function(obj, type) {
        // todo: 
      return obj.wrappedJSObject instanceof type.wrappedJSObject;
    },

    get: function(url, callback) {
      var self = this;
      var opts = {
        method: 'get',
        url: url,
        onerror: function(resp) {
          callback(null);
        },
        onload: function(resp) {
          callback(self.parseXmlHttpResponse(resp));
        }
      };
      GM_xmlhttpRequest(opts);
    },

    parseXmlHttpResponse: function(resp) {
      if (!resp)
        return null;

      var obj = {
        readyState: resp.readyState,
        status: resp.status,
        statusText: resp.statusText,
        response: { body: { text: resp.responseText } }
      };

      obj.response.headers = (function(headers) {
        var obj = {};
        headers.split('\n').forEach(function(header) {
          var pair = header.split(': ');
          if (pair.length != 2)
            return;
          console.log(pair[0]);
          console.log(pair[1]);
          obj[pair[0]] = pair[1];
        });
        return obj;
      })(resp.responseHeaders);

      if (!obj.response.headers['Content-Type'])
        return obj;

      var contentType = obj.response.headers['Content-Type'].split('; ');
      if (contentType[0] == 'application/json') {
        obj.response.body.json = eval(resp.responseText);
        obj.response.body.type = 'json';

      } else if (contentType[0] == 'text/html') {
        // todo: 
        var r = document.createRange();
        r.selectNode(document.documentElement);
        var d = document.implementation.createDocument(null, 'html', null);
        d.documentElement.appendChild(r.createContextualFragment(resp.responseText));
        obj.response.body.document = d;
        obj.response.body.type = 'html';

      } else if (contentType[0] == 'text/xml' || contentType[0] == 'application/xml') {
        obj.response.body.document =
          new DOMParser().parseFromString(resp.responseText, contentType[0]);
        obj.response.body.type = 'xml';

      } else {
        obj.response.body.type = 'unknown';
      }

      return obj;
    }

  });
}