たった600円でオライリー本をKindleで読む。自動化。

注意:ここで紹介する方法よりも、calibreを使う方が簡単で、できあがりも高品質です。

「たった600円でオライリー本をiPadやKindleで読む。すてき。」という記事がすてきです。100冊買っても6万円って。でも、作業がちょっと面倒です。

iPadな人は、「perl – O’ReillyのiPhoneアプリ本からepubをぶっこぬく」にあるスクリプトを使えば、処理の大部分を自動化できます。

Kindleな人は、もう一手間かかるのですが、やはりスクリプトを書いて自動化しておきましょう。(参考:Kindle形式で目次を表示する。epubとの違い。Kindle用の目次を生成するスクリプトを書いた。

以下の作業を一つのスクリプトにまとめます。

  1. toc.ncxからのネームスペースの削除
  2. toc.htmlの生成と登録
  3. iPad用のepubの生成
  4. Kindle用のmobiの生成

準備

perl, zip, unzip, kindlegenにPATHを通しておいてください。Windowsの人は、Cygwinを使うといいでしょう(デフォルトではzipは入らないかもしれないので、インストール時に選択してください)。

使い方

perl ipa2mobi.pl ipaファイル名

Kindleで読み込んだところ

ipa2mobi.plのコード

#!/usr/bin/perl -w

#------------------------------------------------------------------
print "Step 1: http://blog.livedoor.jp/dankogai/archives/51484907.html\n";

use strict;
use warnings;
use File::Basename;
use File::Path;

sub clean{
    File::Path::rmtree('Payload');
}

my $src = shift;
#die "usage:$0 src.ipa [dst.epub]" unless -f $src;
die "usage:$0 src.ipa" unless -f $src;
#my $dst = shift;
my $dst = basename($src);
#if (!$dst){
#    $dst = basename($src);
    $dst =~ s/\w+$/epub/;
#}

system qw/unzip -q/, $src, qw/-x iTunes*/;
die $! if $!;
my $app = <Payload/*.app/book>;
clean and die "No book found in the archive" unless $app;
chdir $app;
my $updir = '../../..';
#system qw/zip -q0X/, "$updir/$dst", 'mimetype';
#system qw/zip -qXr9D/, "$updir/$dst", qw/ META-INF OEBPS/;
#chdir $updir;
#clean;
#system qw{open -a /Applications/iTunes.app}, $dst;


#------------------------------------------------------------------
print "Step 2: Modify toc.ncx\n";

chdir 'OEBPS';

rename 'toc.ncx', 'toc.bak';
open(IN, 'toc.bak');
open(NCX, '> toc.ncx');

while (<IN>) {
  $_ =~ s|<(/{0,1})ncx:|<$1|g;
  print(NCX $_);
}

close(IN);
close(NCX);
unlink 'toc.bak';


#------------------------------------------------------------------
print "Step 3: Create toc.html\n";

use XML::LibXML;

if (! -f 'toc.html') {
sub processNavPoint() {
  my $node=shift;
  my $depth=shift;
  if ($depth==0) { print HTML '<h1>'; }
  printf(HTML "<a href='%s'>%s</a>",
    $node->findvalue('content/@src'),
    $node->findvalue('navLabel/text'));
  if ($depth==0) { print HTML "</h1>\n"; }
  my @navPoints=$node->findnodes('navPoint');
  if ($#navPoints!=-1) {
    print HTML "<ul>\n";
    foreach my $navPoint (@navPoints) {
      print HTML '<li>';
      &processNavPoint($navPoint,$depth+1);
      print HTML "</li>\n";
    }
    print HTML "</ul>\n";
  }
}

  open(HTML, '> toc.html');
  binmode(HTML, ":utf8");
  print HTML <<EOF;
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
<head>
<title>Table of Contents</title>
</head>
<body>
EOF
  my $parser = XML::LibXML->new();
  my $toc = $parser->parse_file('toc.ncx');
  &processNavPoint($toc->findnodes('/ncx/navMap/navPoint'),0);
  print HTML '</body></html>';
  close(HTML);


#------------------------------------------------------------------
print "Step 4: Modify content.opf\n";

  rename 'content.opf', 'content.bak';
  open(IN, 'content.bak');
  open(OPF, '> content.opf');

  while (<IN>) {
    print(OPF $_);
    if (/<item id="cover"/) {
      print(OPF '    <item id="toc" href="toc.html" media-type="text/html"/>'."\n");
    }
    elsif (/<itemref idref="cover"/) {
      print(OPF '    <itemref idref="toc" linear="no"/>'."\n");
    }
    elsif (/<reference href="cover.html"/) {
      print(OPF '    <reference href="toc.html" type="toc" title="Table of Contents"/>'."\n");
    }
  }
  
  close(IN);
  close(OPF);
  unlink 'content.bak';
} # end of if (! -f 'toc.html')


#------------------------------------------------------------------
print "Step 5: Create ePub\n";

chdir '..';
system qw/zip -q0X/, "$updir/$dst", 'mimetype';
system qw/zip -qXr9D/, "$updir/$dst", qw/ META-INF OEBPS/;
chdir $updir;
clean;


#------------------------------------------------------------------
print "Step 6: Create mobi\n";

system qw/kindlegen/, "$dst";

問題

  • 目次の位置が本の最後になってしまいます(ジャンプすればいいのですが)
  • perlの作法、よく知りません。

って書いておけば、誰かがきれいなスクリプトに直してくれるはず。

ゆっくり読みたいときは目に優しいKindleが、開発中にリファレンスとして使いたいときは描画の速いiPadがいいと思います。iPhoneで読むのは大変だと思います。

オライリーとしては、情報弱者を食い物にするのと情報強者にサービスするののバランスを調整することで、利益が最大になればいいのでしょう。

追記:toc.htmlがすでに存在しているときには、step 3, 4を飛ばすようにしました。

追記:目次が最後になってしまう問題が解決しました(コメントに感謝)。

追記:ここで紹介する方法よりも、calibreを使う方が簡単で、できあがりも高品質です。

7 thoughts on “たった600円でオライリー本をKindleで読む。自動化。

  1. syou6162さん
    オライリーのiPhone本で、
    実際にtoc.htmlがあるものがあるのでしょうか。
    私が試しに買った3冊には無かったのですが。
    あるなら直した方がいいですね。

  2. [ガジェット]Kindle用の目次を生成するスクリプトを書いた。

    オライリーのiPhoneアプリからepubを抜き出し、それをさらにKindle形式に変換。epubを抜き出すところまでは、dankogai先生がスクリプ…

  3. 目次の位置が最後になってしまう件ですが、”Step 4: Modify content.opf”のwhileループに以下を追加すれば、表紙の次に目次がくるようになりました。
    elsif (/’.”\n”);
    }

  4. 前の投稿の表示がおかしくなったため、エスケープしたものを再度投稿します。申し訳ありません。
    elsif (/<itemref idref="cover"/) {
    print(OPF ‘ <itemref idref="toc" linear="no"/>’."\n");
    }

  5. Anonymousさん
    掲載したスクリプトを修正しました。
    ありがとうございます。

コメントは停止中です。