Nコードからepubファイルが作れたらいいなと思った。そう思ったのならそうしろってまで俺はもう言う
2012年12月03日 (月) 05:27
【小説家になろう】の作品には小説コード(Nコード)ってのが設定されています。
このコードに対応する短編作品または連載作品を電子書籍用ファイル(.epub)として保存するプログラムを作ってみました。

動作はOS:Linuxのみで確認。たぶんアンドロイドでも動く。rubyが環境中で動くことが前提
windowsでは動かしてないから、動くかわからない。たぶん、ファイルパス指定のところを「/」区切りではなく「\」区切りに変えてあげれば動くと思われる。

【使い方】
1,Ruby1.9をインストールする

2,gepub(指定したファイルをepubに変換する機能)をインストールする
  # gem install gepub

3,下記の[ePubniNarou.ini][ePubniNarou.rb]をそれぞれメモ帳にコピー(--- なんとか ---の行はいれない)。ファイル名を「--- なんとか ---」の何とかで指定された奴にして保存。

4,保存した2ファイルを適当なフォルダにいれる(例として~/ePubniNarou/以下に格納したとする)
  $ cd ~/ePubniNarou
  $ ls 
  ePubniNarou.ini  ePubniNarou.rb
  $ ruby -v
  ruby 1.9.3p0 (2011-10-30 revision 33570) [i686-linux]
  $ ruby ePubniNarou.rb n7148bi
  出力完了!
  $ ls
  ePubniNarou.ini  ePubniNarou.rb  n7148bi.epub
  
【Q&A】
『使ってみたら、家のパソコンがこわれた』『使ったら、保存していたファイルが消えたんですが』など
→利用は自己責任でお願いします。

『epubファイルの表示の仕方がわかんね』
→FireFoxにEPUBReaderいれてみたり、なんかそれっぽいブラウザを探しましょう

『でてきたepubファイルがしょぼい』
→[ePubniNarou.ini]の中を改造してください
  [css/XXXXX] とか書かれているXXXXXクラスのフォーマットを変更します。
  具体的には
  
  ---ePubniNarou.iniに追記---
  #追記内容 ←こんなふうに「#〜」とコメントをかける
  #作者名の表示を変更
  [css/.novel_writername]
  #枠で囲む
  border=dotted orange 10px
  #斜体表示する
  font-style=italic
  ---ePubniNarou.iniに追記ここまで---
  
  すると内部的に
  .novel_writername {
    border:dotted orange 10px;
    font-style:italic;
  }
  というCSSで表現されます。きっと。



記.設定ファイル(.ini)とプログラム(.rb)


--- ePubniNarou.ini ---
[url]
ncode_url=/ncode.syosetu.com/


[css/div.novel_title]
font-size=1.2em
font-weight=bold


[css/.novel_title2]
font-size=1.8em
font-weight=bold

[css/.novel_subtitle]
font-size=1.2em
font-weight=bold

--- ePubniNarou.rb ---
# coding: utf-8
#↑の行が一行目に来るようにメモ帳に貼り付ける

require 'open-uri'
require 'fileutils'
require 'kconv'
require 'gepub'

#情報
$n_auther = ''
$n_title  = ''
$n_update = ''
$styleget_flg = false

#iniファイル読込
#ファイル内の[]で囲まれた要素を第一要素に
#a=b のaを第二要素にして、bの値を格納
#先頭が#のものはコメントと見倣す
def readIniFile(fname)
  if not File.exist?(fname) then
    return nil
  end
  #二次元ハッシュ作成
  res = Hash.new { |hash,key| hash[key] = Hash.new {} }
  #ファイル読込
  File.open(fname, "r") do |file|
    ini_index = "def"
    file.each_line do |line|
      tmpline = line.strip
      if tmpline.to_s.empty? or tmpline[0] == "#" then
        #skip
      elsif tmpline =~ /^\[.*\]$/ then
        ini_index = tmpline.gsub('[', '').gsub(']', '').strip
      elsif tmpline.index("=") then
        res[ini_index][line.split("=")[0].strip] = line.split("=")[1].strip
      end
    end
  end
  return res 
end

#指定されたページを開き、
#ページ内の<div class="novel_bar">または<div class="novel_title">行から
#</div><!--novel_color-->行までをtempfileに出力する
#ただしtmpfileが開けないときと、webページタイトルが「エラー」の時はnilを帰す
def readNarouPage(url , tmpdir)
  begin
    putsflg = nil
    Dir.mkdir(tmpdir)
    File.open("#{tmpdir}/index.html", 'w') { |fline|
      open(url){|fweb|
        fweb.each_line {|webline| #取得して1行ずつ読み出す
          getline = webline.toutf8 #取得はASCII-8BIT形式でされるので文字コード変換しないと日本語比較できない
          #ページ内の<div class="novel_bar">行から</div><!--novel_color-->行までを出力
          if getline =~ /<div class="novel_bar">/ then
            putsflg = 'text'
          elsif getline =~ /<div class="novel_title">/ then
            putsflg = 'title'
          elsif getline =~ /<title>エラー<\/title>/ then
            puts("そのページは存在しません")
            return nil
          elsif getline =~ /<title>.*<\/title>/ then
            fline.write('<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />')
            fline.write(getline) # <title>
            if $styleget_flg then
              styls_place = "../styles.css"
            else
              #タイトルのとき
              styls_place = "./styles.css"
              $styleget_flg = true
            end
            fline.write("<link rel='stylesheet' href='#{styls_place}' type='text/css' media='screen,print'/>")
            fline.write('</head>
<body>')
            $n_title = getline.gsub(/<title>/, '')
            $n_title = $n_title.gsub(/<\/title>/, '')
          elsif getline =~ /作者:<a href=.*\/a>/ then
            $n_auther = getline.gsub(/<\/a>/, '')
            $n_auther = $n_auther.gsub(/.*作者:<a href=.*">/, '')
          elsif getline =~ /<meta name="WWWC" content=.*>/ then
            $n_update = getline.gsub(/" \/>.*/, '')
            $n_update = $n_update.gsub(/<meta name="WWWC" content="/, '')
            $n_update = $n_update.gsub(/ /, 'T') + 'Z'
            $n_update = $n_update.gsub(/\//, '-') + ':00Z'
          elsif getline =~ /<\/div><!--novel_color-->/ then
            break
          end
          if putsflg == 'title' then
            getline.gsub!(/<a href="\/#{$code_id}/, '<a href=".')
            fline.write(getline)
          elsif putsflg == 'text' then
            getline.gsub!(/<a href="\/#{$code_id}/, '<a href="..')
            fline.write(getline)
          end
        }
      }
      fline.write("</body></html>")
    }
    page = 0
    if putsflg == 'title' then
      while readNarouPage("#{url}/#{page + 1}/","#{tmpdir}/#{page + 1}")
        page += 1
      end
    end
    return page
  rescue
    #余計につくられたフォルダを削除
    #puts "rm_r " +  tmpdir 
    FileUtils.rm_r( tmpdir,:force=>true)
    return nil
  end
end

#与えられた情報からCSSファイルを作る
#set_hashの中で、キーが「css/XXX」のとき、XXXの要素名に対してメンバーを設定する
def makeEpubCss(fpath,set_hash)
  begin
    #ひたすら書込み
    File.open(fpath,'w'){|cssf|
      #変数がハッシュかを判定
      if not set_hash.instance_of?(Hash) then
        return nil # abnomal end
      end
      
      set_hash.each {|element,attr_hash|
        if element =~ /^css\// then
          if not attr_hash.instance_of?(Hash) then
            break
          end
          #要素数が1以上の時にやりましょう
          if attr_hash.length > 0 then
            cssf.write("#{element.split('/')[1]}{\n")
            attr_hash.each {|prop,val|
              val = val.downcase
              if val == "true" then
                #多分boldなど想定
                cssf.write("\t#{prop};\n")
              elsif val != "false" then
                cssf.write("\t#{prop}: #{val};\n")
              end
            }
            cssf.write("}\n")
           end
        end
      }
    }
  rescue
     return nil # abnomal end
  end
end


################################################################################
#ここから下
#メイン処理開始
################################################################################

#コマンドラインから渡された引数をもらう(アルファベットは小文字に変換)
$code_id = ARGV[0].downcase
if $code_id == nil  or ( $code_id[0] != 'n') then
  puts("nで始まるパラメータを入力してください")  #sコード(シリーズ物)には未対応
  exit
end

#ePubniNarou.iniファイルの読込
pgpath = File.dirname(__FILE__) 
ini_set = readIniFile("#{pgpath}/ePubniNarou.ini")
#一時作業フォルダ作成
workdir = ""
workdir << pgpath << "/ePubniNarou_temp"
if Dir.exist?(workdir) then
  FileUtils.rm_r(workdir,:force=>true)
end
epubdir = "#{workdir}/#{ARGV[0]}"
Dir.mkdir(workdir)

open_uri = "http:/"
open_uri << ini_set['url']['ncode_url'] << ARGV[0] 
page = readNarouPage(open_uri,epubdir) # 短編だと0が帰ってくる。連作だと話数が帰ってくる(最小1)

#CSSをつくる
if not makeEpubCss("#{epubdir}/styles.css",ini_set) then
  puts 'CSSファイルの作成になんか失敗した'
end

#ページが0以上の数なら
if page then
  builder = GEPUB::Builder.new {
    language 'ja'
    unique_identifier open_uri, 'BookID', 'URL'
    title $n_title
    subtitle '[ePubになろう]により作成'

    creator $n_auther

    contributors 'Denshobu', 'Asagaya Densho', 'Shonan Densho Teidan', 'eMagazine Torutaru'

    date $n_update

    resources(:workdir => "#{epubdir}/") {
      #cover_image 'img/image1.jpg' => 'image1.jpg'
      ordered {
        #目次の作成
        if page == 0 then
          #短編の場合:チャプターとかはいらない
          file 'index.html'
        else
          i = 0
          while i < page do
            if i == 0 then
              file 'index.html'
              heading 'Top'
            else
              file "#{i}/index.html"
              heading "Chapter #{i}"
            end
            i += 1
          end
        end
        file 'styles.css'
      }
    }
  }
  builder.generate_epub("#{pgpath}.epub")
end



#作業フォルダを削除
FileUtils.rm_r(workdir,:force=>true)

#終了メッセージ
puts "出力完了!"
コメント全1件
コメントの書き込みはログインが必要です。
nisho
2012年12月05日 00:11
オープンソースと言うよりも、書き捨てのソースですw