从Amazon Cloud Reader导出离线电子书

原文地址:http://sakinijino.com/archives/4022

功能:通过Amazon Cloud Reader,获得已购买书籍的原文。

步骤:

访问 https://read.amazon.com/
右键想获得原文的书,选择Download & Pin
等待下载结束,书上出现绿色别针
点击进入阅读
打开链接。把其中文本复制到Amazon Reader的地址栏中
(Safari下过;但在Chrome下测试出现诡异的错误。如果Chrome没效果,可以按F12进入Console Tab,把“javascript:”后面的部分粘贴到命令行中)
等一会儿,然后Ctrl-A & Ctrl-C
回馈社会

原理:Kindle Reader用了localStorage database来存储Pin到本地书籍,这些数据库中的内容除了被lz压缩外,没有特殊处理,所以只要把数据取出来解压缩即可。

不足:不能获得图片(图片也是被放进database的,实现起来不难,只是我懒得改了);文本是带html标签的,直接贴到支持html标签的编辑器中可能会出问题。

原文源代码


(function (){
function getContent(cb){
  function getAsin(cb){
    var title = $("#KindleReaderIFrame")[0].contentWindow.document.getElementById("kindleReader_title").innerHTML;
    KindleDBClient.getAppDb().getTable('bookdata').getRecord({title:title}).done(function(e){cb(e[0].asin)});
  };
  function getF(asin, cb){
    var bookdb =$("#KindleReaderIFrame")[0].contentWindow.KindleReaderBookInfoProviderDB.BookInfoDB(asin);
    bookdb.getFragmentIds().done(function(e){
      bookdb.getFragments(e).done(cb);
    })
  };
  function getMD(asin, cb){KindleDBClient.getBookDb().getTable("bookinfo").getRecord({asin:asin}).done(function(e){cb(e[0].metadata)})};

  getAsin(function(asin){
  getF(asin, function(fragments){
  getMD(asin, function(md){
    var kc = $("#KindleReaderIFrame")[0].contentWindow.KindleCompression;
    c = {};
    kc.lzAddStringsToDictionary(md.cpr, c);
    kc.lzAddNumbersToDictionary(c);
    d = kc.lzGetDecompressionDictionary(c);
    content = "";
    for (var i=0; i<fragments.length; ++i)
      if (fragments[i]) content += kc.lzExpandWithStaticDictionary(fragments[i].fragmentData, d, 256);
    cb(content);
  })})})
};
getContent(function(c){document.body.innerHTML = c});
})();

以下为亦忧更新部分:

在Chrome中运行,有的书会报错。Uncaught TypeError: Cannot read property ‘asin’ of undefined。找了下原因。

有的书网页上的书名和缓存数据库中的书名不匹配,导致代码第四行title在数据库查询中返回空对象undefined。

修正:
1、Chrome中按ctrl+shift+j,进入开发模式。Resources下找到Databases>K4W>bookdata,将title列中的值替换第四行代码。即:
var title = $(“#KindleReaderIFrame”)[0].contentWindow.document.getElementById(“kindleReader_title”).innerHTML;
改为
var title = “Robin Nixon’s PHP Crash Course: Learn PHP in 14 easy lessons”;(比如这本书)

Chrome可以直接保存文件为完整的HTML。默认滚动条会被隐藏。用文本编辑器打开保存的文件,去掉头上没用的HTML标签,就可以方便阅读了。

继续更新:

2、发现下载有的书出现第二个错误,Uncaught TypeError: Object [object Window] has no method ‘getBookDb’。将第8行代码修改了一下。即:
var bookdb =$(“#KindleReaderIFrame”)[0].contentWindow.KindleReaderBookInfoProviderDB.BookInfoDB(asin);
改为
var bookdb = KindleDBClient.getBookDb().BookInfoDB(asin);

3、又发现第三个错误,TypeError: Object # has no method ‘getAppDb’。查了一下KindleDBClient的值似乎丢失了,找不到相应的函数调用。简单的解决方法:将KindleDBClient替换为KindleModuleManager.getModuleSync(KindleModuleManager.DB_CLIENT)

20140605更新:
4、发现#2号更新失效,再次改为:
var bookdb = KindleModuleManager.getModuleSync(KindleModuleManager.DB_CLIENT).getBookDb().BookInfoDB(asin);

修改后的代码如下:


(function (){
function getContent(cb){
  function getAsin(cb){
    var title = $("#KindleReaderIFrame")[0].contentWindow.document.getElementById("kindleReader_title").innerHTML;
    KindleModuleManager.getModuleSync(KindleModuleManager.DB_CLIENT).getAppDb().getTable('bookdata').getRecord({title:title}).done(function(e){cb(e[0].asin)});
  };
  function getF(asin, cb){
    var bookdb = KindleModuleManager.getModuleSync(KindleModuleManager.DB_CLIENT).getBookDb().BookInfoDB(asin);
    bookdb.getFragmentIds().done(function(e){
      bookdb.getFragments(e).done(cb);
    })
  };
  function getMD(asin, cb){KindleModuleManager.getModuleSync(KindleModuleManager.DB_CLIENT).getBookDb().getTable("bookinfo").getRecord({asin:asin}).done(function(e){cb(e[0].metadata)})};

  getAsin(function(asin){
  getF(asin, function(fragments){
  getMD(asin, function(md){
    var kc = $("#KindleReaderIFrame")[0].contentWindow.KindleCompression;
    c = {};
    kc.lzAddStringsToDictionary(md.cpr, c);
    kc.lzAddNumbersToDictionary(c);
    d = kc.lzGetDecompressionDictionary(c);
    content = "";
    for (var i=0; i<fragments.length; ++i)
      if (fragments[i]) content += kc.lzExpandWithStaticDictionary(fragments[i].fragmentData, d, 256);
    cb(content);
  })})})
};
getContent(function(c){document.body.innerHTML = c});
})();
Advertisements