Hatena::Groupbugrammer

蟲!虫!蟲!

Esehara Profile Site (by Heroku) / Github / bookable.jp (My Service)
過去の記事一覧はこちら

なにかあったら「えせはら あっと Gmail」まで送って頂ければ幸いです。
株式会社マリーチでは、Pythonやdjango、また自然言語処理を使ったお仕事を探しています

 | 

2011-10-31

[]Project Eulerの問題集を作るついでにEpubのおさらいをする 01:24

 f:id:nisemono_san:20111101012558p:image

 テストは、オンラインでepubが読める、Bookwormで。

 以前にも青空文庫の作家別作品リストをepub形式にまとめ直してくれるスクリプト - 蟲!虫!蟲! - #!/usr/bin/bugrammer というスクリプトは書いていて、アンドロイドアプリによっては文字化けするということを除けば、それなりに可動していたんだけど(見直したら全然上手くいってなかった)、このとき作ったスクリプトはそれほどEpubのことを理解していたか、といえばそういうわけではなかったと思うので、改めてEpubのことをおさらいしながら書こうかと思う。

 今回作るのは、Project Eulerの問題集をまとめたやつでも作ろうと思ってます。

 Project Euler - PukiWiki

そもそもEpubとは何か。

 zipファイル形式で圧縮したものを拡張子epubというのをくっ付けているだけ。というのはあんまりか。

 実際のところ、下の受け売りなので、あまり期待しないほうがいいかなーとは思う。

 Database Error

まず、必要なファイル構成を作る

 実際は下を見てもらうのがわかりやすいと思う。

 Database Error

 で、最初に作れるファイル構成は次の通り。

 ディレクトリは最初に作れる、というか作っておかないといけないのが当然だとして、では"minetype"と"container.xml"とは何か、という話にになる。

mimetype

 mimetypeの目的は「このファイルがEpubなんですよ」ということをお知らせすること。なので内容は固定で非常にシンプル。

application/epub+zip

container.xml

 opfファイルのパスを指定するためのファイル。とくに何も考えずに下のようにしてあげればいいのではないかなと思う。

<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
  <rootfiles>
    <rootfile full-path="hogehoge.opf" media-type="application/oebps-package+xml" />
  </rootfiles>
</container>

なぜopfはあとから作るのか

 まだスクリプト自体は、どのようなコンテンツが入るのかはわかっていない。opfファイル、及びncx(あとから説明します)は、実際のコンテンツは何処にあるのか、などを指定するものだったりするので、ファイルが揃ったと同時にzipファイルにしてやる必要がある。

したがって、クラスの初期化する条件は決定する

 実装的にはこんな感じで作ってあげればいいだろう。

class Epub_maker(object):
    def __init__(self,filename,title,author,identi,workspace='./temp/',):
        """
        Initialize :: Create Directory and File.Epub needs there.
        ./(workspace)/
        ./(workspace)/mimetype
        ./(workspace)/META-INF/container.xml
        """
        self.filename = filename
        self.title = title
        self.author = author
        self.identi = identi
        workdir = workspace + filename + "/"
        
        #Create Directory
        os.mkdir(workdir)
        os.mkdir(workdir + "OEBPS")
        os.mkdir(workdir + "META-INF")
        
        #Create Minetype
        mimetype = open(workdir + "mimetype","w")
        mimetype.write("application/epub+zip\n")
        mimetype.close()

        #Create xml
        container_xml = open(workdir + 'META-INF/container.xml','w')
        make_xml = """<?xml version='1.0' encoding="UTF-8"?>
        <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
            <rootfiles>
                <rootfile full-path="content.opf" media-type="application/oebps-package+xml" />
            </rootfiles>
        </container>
        """
        container_xml.write(make_xml)
        container_xml.close()
        self.workdir = workdir
   
        """
        container_xml.write(make_xml)
        container_xml.close()
        self.workdir = workdir

optファイルなどの生成の前に、スクレイピングしよう。

 今度のスクレイピングはちょっとややこしくて、二段がまえになっている。まずリンクを習得し、そのあとにリンク先をスクレイピングしてあげなければならない。とはいえ、それほど難しくは無い。BeautifulSoupならね!

 内部のEpubがどういう風になるのか、については難しくないように思える。xmlとはいえ、基本的にはHTMLだからだ。ただ気をつけておかなきゃいけないことが一点だけあるとするならば、日本語の指定をちゃんとしておかないと、あとあとおかしくなるという点。あと、オーサリングツール使いましょう、みたいな人が多くて、実際のXHTMLはどういう風なテンプレートになるのか、というところに関して曖昧なところが多かった。この点に関しては、下のサイトがわかりやすかった。

 Epub Format Construction Guide - HXA7241 - 2007

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//JP" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

 あと注意しなければいけないのは、ちゃんと言語を指定しないとズレるという点。この点に関しては、下のサイトを見ること。

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">

 こうしてあげないと文字化けするっぽいよ。

 で、xmlを作る部分に関しては、このように指定。

        opener = urllib2.build_opener()
        header = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//JP" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
            <head>
        '''

        target_page = opener.open(page).read()
        soup = BeautifulSoup.BeautifulSoup(target_page)
        title = soup.find("h1").text
        body_str = ""
        for num,body_soup in enumerate(soup.findAll("p")):
            if num + 1 == len(soup.findAll("p")):
                break
            body_str += "<p>%s</p>\n" % body_soup.text
        
        header += '''
            <title>%s</title>
        </head>
        <body>
        ''' % title

        body = '''
        <h1>%s</h1>
        %s
        ''' % (title,body_str)

        fooder = '''
        </body>
</html>
        '''
        return (header + body + fooder)

 Project Eularは、本当は画像があるんだけど、その点に関しては手を抜いてスクレイピングしていないので、ご愛嬌。

opfとncxは前の奴をそのまま流用

 詳しい話は仕様書を見てもらうとして、ここではざっと解説。

no title

とりあえず、<package>にはふたつのセクションがある。それは<metadata>と<manifest>だ。<metadata>は、Epub全体のデータになる。例えば

<dc:title>
書籍タイトル
<dc:creator opf:rote="aut">
著作者
<dc:language>
言語
<dc:publisher>
出版者
<dc:identifier>
本のID

 本のIDってのもようわからんが、これは任意の値を当てはめればいい。俺はURLを埋め込んでいたけど、とくに問題はなさそうなので、そういう風にしている。<manifest>はデータのありかを示すためのものだ。なので、アイテムIDと、場所と、メディアタイプを指定しなくちゃいけない。例えば下のようにね。

<item id="hogefile" href="hoge/text.html" media-type="application/xhtml+xml" />

 ncxは上記のソースと結びつけていく作業と考えるのが一番わかりやすいのかな、とは思う。このあたりがわかりやすいかなーと思う。

 電子書籍 ePub 電子書籍の作り方2 シンプル ePUB フォーマット ガイド :How to create an ePub ebook? Simple ePUB Format Guide | Satirical Itami.info : 風刺的伊丹.情報

そんな感じで完成

 というわけで、完成はした。とりあえず、出来上がったもので満足したので、それでいいかなーとは思った。あと、やっぱり何かを自動生成するスクリプトは、その形式に対する仕様の理解が深まるのがいいなーとは思います。

 今日のソースはこちら。

 Project Euler (Japanese Translation) -> Epub ? GitHub

KlonenganKlonengan2012/02/27 16:23If I cmonmuicated I could thank you enough for this, I'd be lying.

nwslkubpvtbnwslkubpvtb2012/02/28 03:51nCaWIg <a href="http://datkpohtrvjw.com/">datkpohtrvjw</a>

smkwlibsmkwlib2012/02/28 19:49MS9KOf , [url=http://bvthzmgdxdpl.com/]bvthzmgdxdpl[/url], [link=http://kzplmwrbljqd.com/]kzplmwrbljqd[/link], http://fngsyboowoiu.com/

uyliucjavuyliucjav2012/03/02 00:336xkRHi <a href="http://xduatvxzikub.com/">xduatvxzikub</a>

mklhbtmbsmklhbtmbs2012/03/14 09:53R9opQH , [url=http://ntudrzlekjjs.com/]ntudrzlekjjs[/url], [link=http://ytyrxjvyykrd.com/]ytyrxjvyykrd[/link], http://mjggkrlbwqdl.com/

 |