PythonでMarkdownからEPUBをつくろう

この連載について

 この連載は、『PythonでMarkdownからEPUBをつくろう』を一部抜粋して編集したものです。

 本編には、全てのソースコードや、生成する原稿のサンプルが付属しています。ぜひ、こちらもご購入ください。

 姉妹版の『PythonとPygameで作る レトロ風RPG 全コード』もあります。

目次に戻る

10-1 圧縮してEPUB生成

公開:2025-05-29

 mod/pack/zip.pyでは、作成した02_epub/epubディレクトリーの中身を、ZIP形式で圧縮して02_epub/py-md-epub.epubファイルを作ります。

 圧縮は、Python標準ライブラリーのzipfileを使います。先頭に無圧縮でmimetypeを格納して、以降は全てのファイルを圧縮しながら追加していきます。

▶ EPUBファイルの圧縮

 それではファイル構成とプログラムを示します。

▶ ファイル構成
▶ main.py
 1import os, zipfile
 2from pathlib import Path
 3from mod.util import file as uf
 4
 5# ZIPで固めてEPUBを出力
 6def gen(data):
 7    dir = data['dirs']
 8    con = data['config']
 9
10    # すでにあればゴミ箱に
11    uf.exist2trash(con['pEpub'])
12
13    # ZIPで圧縮してEPUBを作る
14    with zipfile.ZipFile(con['pEpub'], 'w',
15            compression = zipfile.ZIP_DEFLATED,
16            compresslevel = 9) as zf:
17        append_mimetype(zf, dir)    # mimetypeの格納
18        append_rest(zf, dir)        # 残りのファイルの格納
19
20# mimetypeの格納(先頭に無圧縮で追加する必要がある)
21def append_mimetype(zf, dir):
22    p = uf.join(dir['outputEpub'], 'mimetype')
23    zf.write(p, 'mimetype', compress_type=zipfile.ZIP_STORED)
24
25# 残りのファイルの格納
26def append_rest(zf, dir):
27    for d_this, _dirs, files in os.walk(dir['outputEpub']):
28        # 相対パスを用意
29        d_rel = str(Path(d_this).relative_to(dir['outputEpub']))
30        #print(d_this, _dirs, files, d_rel)
31
32        # ルートでないならディレクトリーを作る
33        if d_this != dir['outputEpub']:
34            zf.mkdir(d_rel)
35
36        # ファイルの処理
37        for name in files:
38            # 格納済は飛ばす
39            if name == 'mimetype': continue
40
41            # ファイルを格納
42            p = os.path.join(d_this, name)
43            arcname = os.path.join(d_rel, name)
44            zf.write(p, arcname)
45            print(f'- {arcname}')

 少し長いので分割して説明します。

インポート部分

 まずはインポート部分です。

1import os, zipfile
2from pathlib import Path
3from mod.util import file as uf

 ファイルのZIP圧縮にはPython標準ライブラリーのzipfileを利用します。

 相対パスを求める必要があるので、Python標準ライブラリーのpathlibPathを読み込みます。

ZIPで固めてEPUBを出力

 続いて、ZIPでファイルを固めてEPUBを出力する処理です。詳細部分は別の関数でおこないます。

 5# ZIPで固めてEPUBを出力
 6def gen(data):
 7    dir = data['dirs']
 8    con = data['config']
 9
10    # すでにあればゴミ箱に
11    uf.exist2trash(con['pEpub'])
12
13    # ZIPで圧縮してEPUBを作る
14    with zipfile.ZipFile(con['pEpub'], 'w',
15            compression = zipfile.ZIP_DEFLATED,
16            compresslevel = 9) as zf:
17        append_mimetype(zf, dir)    # mimetypeの格納
18        append_rest(zf, dir)        # 残りのファイルの格納

 まず、EPUBファイルがすでにある場合はゴミ箱に送ります。

 次にwith文でZIPファイルを開きます。このときの設定は、パスはcon['pEpub']、モードは書き込みモードでw、圧縮方式compressionzipfile.ZIP_DEFLATED(通常のZIP圧縮)、圧縮レベルcompresslevelは最大の9です。

▶ ZIPファイルを開く設定

 この設定で開いたZIPファイルを変数zfで操作可能にします。

 続いて2つの処理をおこないます。「mimetypeの格納」と「残りのファイルの格納」です。このあとは、この2つの処理を見ていきます。

mimetypeの格納

 mimetypeファイルを、ZIPファイルの先頭に無圧縮で追加します。

20# mimetypeの格納(先頭に無圧縮で追加する必要がある)
21def append_mimetype(zf, dir):
22    p = uf.join(dir['outputEpub'], 'mimetype')
23    zf.write(p, 'mimetype', compress_type=zipfile.ZIP_STORED)

 ZIPファイルへのファイルの追加はzf.write()関数でおこなえます。関数の引数は、第1引数が追加するファイルのパス、第2引数がZIPファイル内のパスです。

 第1引数は、ローカルファイルの絶対パス~02_epub/epub/mimetypeを指定しています。第2引数は、ファイル名のmimetypeを指定しています。

 続いて引数にcompress_type=zipfile.ZIP_STOREDと指定します。この設定を加えると、無圧縮でファイルを追加します。

残りのファイルの格納

 mimetype以外のファイルを、ZIPファイルに追加します。ここではcompress_typeの指定をおこないません。そのため、ファイルを開いたときの圧縮設定でファイルは追加されます。

25# 残りのファイルの格納
26def append_rest(zf, dir):
27    for d_this, _dirs, files in os.walk(dir['outputEpub']):
28        # 相対パスを用意
29        d_rel = str(Path(d_this).relative_to(dir['outputEpub']))
30        #print(d_this, _dirs, files, d_rel)
31
32        # ルートでないならディレクトリーを作る
33        if d_this != dir['outputEpub']:
34            zf.mkdir(d_rel)
35
36        # ファイルの処理
37        for name in files:
38            # 格納済は飛ばす
39            if name == 'mimetype': continue
40
41            # ファイルを格納
42            p = os.path.join(d_this, name)
43            arcname = os.path.join(d_rel, name)
44            zf.write(p, arcname)
45            print(f'- {arcname}')

 ディレクトリ内のパスはos.walk()関数で次々に得ます。

 for d_this, dirs, files in os.walk(ディレクトリAのパス):のように書くと、ディレクトリA内を次々と走査していきます。

 そして、d_this(現在のディレクトリ)、dirs(現在のディレクトリ内にあるディレクトリのリスト)、files(現在のディレクトリ内にあるファイルのリスト)を得られます。

▶ os.walk()関数で次々に得るタプル

 Path(パス).relative_to(ルートのパス)関数で相対パスを得ます。この戻り値はテキストではないので、str()関数でテキストに変換します。

 次に、現在のディレクトリが、ルートのディレクトリでないならZIPファイル内にディレクトリを作ります。ディレクトリの作成は、zf.mkdir(ディレクトリのパス)でおこないます。

 そしてリストfilesを使い、ファイルを1つずつ追加していきます。ファイルの格納は、zf.write(パス, 格納の相対パス)でおこないます。このとき、mimetypeは追加しないようにします。


目次に戻る

この連載について

 この連載は、『PythonでMarkdownからEPUBをつくろう』を一部抜粋して編集したものです。

 本編には、全てのソースコードや、生成する原稿のサンプルが付属しています。ぜひ、こちらもご購入ください。

 姉妹版の『PythonとPygameで作る レトロ風RPG 全コード』もあります。

 技術系同人誌など まとめページ


トップページに戻る
Cronus Crown(クロノス・クラウン)のトップページに戻る
(c)2002-2025 Cronus Crown (c)1997-2025 Masakazu Yanai
ご意見・お問い合わせはサイト情報 弊社への連絡までお願いします
個人情報の取り扱い、利用者情報の外部送信について