PythonでMarkdownからEPUBをつくろう

この連載について

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

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

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

目次に戻る

8-2 Markdownの変換

公開:2025-05-29

 mod/doc/md.pyでは、Markdownの変換をおこないます。

▶ ファイル構成
▶ mod/doc/md.py
 1import re, markdown
 2
 3# Markdownの初期化
 4md = markdown.Markdown(extensions=[
 5    'attr_list',    # 属性追加
 6    'tables',       # GitHub形式のtable領域
 7    'fenced_code',  # ```~```形式のコード領域
 8    'md_in_html',   # HTMLタグ内にMarkdownを記述
 9    'sane_lists',   # ulとolを別のリストに
10])
11
12# Markdownの変換
13def convert(t):
14    # Markdown変換
15    # 全角インデントが消されるので回避して復帰する
16    t = re.sub('(?m)^( +)', '\b\1\b', t)  # 回避
17    t = md.convert(t)
18    t = re.sub('\b( +)\b', '\1', t)   # 復帰
19
20    # tableタグの位置揃えでstyleが使われるのでclassに変換
21    t = re.sub('style="text-align: (.+?);"', r'class=""', t)
22
23    # 画像のみの行を調整
24    t = re.sub('<p>(<img .+?>)</p>', r'<div class="image-full"></div>', t)
25    return t

 このファイルは2つの部分に分かれています。前半が初期化で、後半が実際の変換です。そのため分けて解説します。

初期化

 まずは初期化部分です。

 1import re, markdown
 2
 3# Markdownの初期化
 4md = markdown.Markdown(extensions=[
 5    'attr_list',    # 属性追加
 6    'tables',       # GitHub形式のtable領域
 7    'fenced_code',  # ```~```形式のコード領域
 8    'md_in_html',   # HTMLタグ内にMarkdownを記述
 9    'sane_lists',   # ulとolを別のリストに
10])

 pip install MarkdownでインストールしたMarkdownパッケージをimport markdownでインポートします。

 ここでは設定を引数にして初期化したあと変換に利用します。事前の初期化はmd = markdown.Markdown(extensions=[])でおこなえます。以降は、変数mdを利用して変換をおこなえます。

 初期化の際に、いくつかの拡張機能を有効にします。ここで利用する拡張機能をまとめておきます。

 変換したHTMLに属性を追加できます。属性は次のように書きます。

要素{: #someid .someclass somekey='some value' }

 #someidid="someid"に、.someclassclass="someclass"に、somekey='some value'はそのまま、HTMLタグの属性として追加されます。

GitHub形式のtable領域を使えるようにします。table領域は次のように書きます。

|ヘッダー1|ヘッダー2|ヘッダー3|
|:--|:--|:--|
|セル1-1|セル1-2|セル1-3|
|セル2-1|セル2-2|セル2-3|

 :--は列の左寄せ、:--:は中央、--:は右寄せを意味します。

 バックスラッシュ3つで囲んだコード領域を使えるようにします。

```
複数行のコード領域。
複数行のコード領域。
```

 先頭のバックスラッシュ3つのあとに、属性の情報を追加できます。

``` { .lang #myid .class1 .class2 style="color: #333" }
print('rat')
print('cat')
print('dog')
```

 .langは、生成される<code>タグに、class="language-lang"の属性を加えます。

 2つめ以降のクラス(.class1.class2)は、生成される<pre>タグに、class="class1 class2"の属性を加えます。

 #myidは、生成される<pre>タグに、id="myid"を加えます。

 IDとクラスの記述順は自由です。

 上のコード領域は、次のように変換されます。

<pre id="myid" class="class1 class2"><code class="language-lang">print('rat')
print('cat')
print('dog')
</code></pre>

 コードにclass="language-lang"の属性を加えるだけなら、次の書き方も使えます。

``` lang
```

 HTMLタグ内にMarkdownを書けるようになります。この機能を使うには、HTMLタグにmarkdown="1"という属性を書く必要があります。

 <ul>のリスト(箇条書きリスト)と<ol>のリスト(連番リスト)が混在して並んでいるときは、別のリストとしてまとめます

 デフォルトでは、先に書いたものに統合されてしまいます。

変換

 変換はmd.convert(t)でおこなえます。ただ、そのままでは問題があるので、いくつか処理を加えています。

11
12# Markdownの変換
13def convert(t):
14    # Markdown変換
15    # 全角インデントが消されるので回避して復帰する
16    t = re.sub('(?m)^( +)', '\b\1\b', t)  # 回避
17    t = md.convert(t)
18    t = re.sub('\b( +)\b', '\1', t)   # 復帰
19
20    # tableタグの位置揃えでstyleが使われるのでclassに変換
21    t = re.sub('style="text-align: (.+?);"', r'class=""', t)
22
23    # 画像のみの行を調整
24    t = re.sub('<p>(<img .+?>)</p>', r'<div class="image-full"></div>', t)
25    return t

 Markdownパッケージは、本文の冒頭にある半角スペースと全角スペースを除去します。日本語の文章を書くときに、全角スペースで1文字下げている場合には、この全角スペースが全てなくなってしまいます。

 この除去を回避するための処理を加えてます。

1t = re.sub('(?m)^( +)', '\b\1\b', t)  # 回避
2t = md.convert(t)
3t = re.sub('\b( +)\b', '\1', t)   # 復帰

 上の1行目と3行目がその処理です。正規表現の置換re.sub()を使い、先頭が全角スペース1つ以上の場合は\b(ベル)で囲います。そしてMarkdown変換後に\bを除去します。

 1行目で出てくる(?m)^$の記号の挙動を変えるフラグです。通常では^は文章全体の先頭を、$は文章全体の末尾を表します。(?m)フラグを付けると、^は各行の先頭を、$は各行の末尾を表します。

 また( )(丸括弧)で囲んだ部分は、置換テキスト内で利用できます。1つめの丸括弧内は\1、2つ目の丸括弧内は\2として利用できます。

 続いて、style="text-align: right;"のような属性部分をclass="right"のように置換します。

 これはMarkdownパッケージが、テーブルを作る際に、左寄せ、中央、右寄せの設定を直接style属性に書き込むためです。この書き方はKindleでエラーが出るために修正しています。

 最後に、画像のみの行のタグを下のように調整しています。


目次に戻る

この連載について

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

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

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

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


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