この連載は、『PythonでMarkdownからEPUBをつくろう』を一部抜粋して編集したものです。
本編には、全てのソースコードや、生成する原稿のサンプルが付属しています。ぜひ、こちらもご購入ください。
姉妹版の『PythonとPygameで作る レトロ風RPG 全コード』もあります。
≫ 目次に戻る mod/pack/zip.pyでは、作成した02_epub/epubディレクトリーの中身を、ZIP形式で圧縮して02_epub/py-md-epub.epubファイルを作ります。
圧縮は、Python標準ライブラリーのzipfileを使います。先頭に無圧縮でmimetypeを格納して、以降は全てのファイルを圧縮しながら追加していきます。

それではファイル構成とプログラムを示します。
gen-epub/main.pyconfig.yamlmod/pack/zip.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標準ライブラリーのpathlibのPathを読み込みます。
続いて、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、圧縮方式compressionはzipfile.ZIP_DEFLATED(通常のZIP圧縮)、圧縮レベルcompresslevelは最大の9です。
con['pEpub']。config.yamlのpEpubに書いたパス。w。compressionの設定。zipfile.ZIP_DEFLATED(通常のZIP圧縮)。compresslevelの設定。9。 この設定で開いたZIPファイルを変数zfで操作可能にします。
続いて2つの処理をおこないます。「mimetypeの格納」と「残りのファイルの格納」です。このあとは、この2つの処理を見ていきます。
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(現在のディレクトリ内にあるファイルのリスト)を得られます。
d_thisdirsfiles Path(パス).relative_to(ルートのパス)関数で相対パスを得ます。この戻り値はテキストではないので、str()関数でテキストに変換します。
次に、現在のディレクトリが、ルートのディレクトリでないならZIPファイル内にディレクトリを作ります。ディレクトリの作成は、zf.mkdir(ディレクトリのパス)でおこないます。
そしてリストfilesを使い、ファイルを1つずつ追加していきます。ファイルの格納は、zf.write(パス, 格納の相対パス)でおこないます。このとき、mimetypeは追加しないようにします。
この連載は、『PythonでMarkdownからEPUBをつくろう』を一部抜粋して編集したものです。
本編には、全てのソースコードや、生成する原稿のサンプルが付属しています。ぜひ、こちらもご購入ください。
姉妹版の『PythonとPygameで作る レトロ風RPG 全コード』もあります。