Djangoで使いたいからPythonでmarkdownをHTML変換する
目的
markdownをHTML変換
ブログみたいなWEBアプリを開発する際にmarkdownで記事を書いてそれをHTMLに変換して表示という風にやりたい。
やることは以下です。
- DBにcontentsにmarkdown形式で記事を保存
- markdownをHTMLに変換
- 実際に表示
1.の部分は本記事では触れません。
今回やること
やることとしては以下になります。
- MarkdownからHTMLへの変換
- テンプレートで使うためにタグを作成
- テンプレートに埋め込み
1.MarkdownからHTMLへの変換
必要なライブラリをインストールします(まだインストールしていない場合)
pip install markdown pygments
サンプルコード
import markdown
from markdown.extensions.codehilite import CodeHiliteExtension
from markdown.extensions.toc import TocExtension
from markdown.extensions.sane_lists import SaneListExtension
from markdown.extensions.nl2br import Nl2BrExtension
from markdown.extensions.fenced_code import FencedCodeExtension
from markdown.extensions.wikilinks import WikiLinkExtension
from markdown.extensions.footnotes import FootnoteExtension
from markdown.extensions.tables import TableExtension
text = """
[TOC]
# Title
Here's a brief introduction about the topic.
## Subheading 2
Here's some content for subheading 2. This is a footnote[^1].
### Sub-subheading 3
Some additional details for this sub-subheading.
Here's a simple list:
1. Item 1
3. Item 2
```python
print("This is a code snippet using fenced code.")
```
New lines
will be
converted to breaks.
[[WikiLink]]
Here's a table:
| Header 1 | Header 2 |
| -------- | -------- |
| Row1Col1 | Row1Col2 |
| Row2Col1 | Row2Col2 |
## Subheading 2
Another subheading with different content.
[^1]: This is the footnote content.
"""
# 使用する拡張のリスト
extensions = [
CodeHiliteExtension(),
TocExtension(marker="[TOC]"),
SaneListExtension(),
Nl2BrExtension(),
FencedCodeExtension(),
WikiLinkExtension(base_url="/wiki/", end_url=".html"),
FootnoteExtension(),
TableExtension()
]
# Markdown を HTML に変換
html = markdown.markdown(text, extensions=extensions)
print(html)
補足:拡張が必要なタグ
普通のhタグpタグは変換されますがtableなどは拡張しないといけないみたいです。
以下に拡張が必要なタグの例を示します。
詳しく知りたい方は公式ドキュメントを見てください。
了解しました。まず、上記の拡張の特徴を表形式でまとめます。
拡張名 | 特徴 |
---|---|
tables | Markdown のテーブル構文をサポート。 |
nl2br | 通常の改行 (\n ) を HTML の <br> に変換。 |
fenced_code | 3つのバックティック (``` ) で囲まれたコードブロックをサポート。 |
codehilite | Pygments を使用してコードのハイライトをサポート。 |
wikilinks | Wiki スタイルのリンク [[]] をサポート。 |
toc | マークダウン文書の目次 (Table of Contents) の生成をサポート。 |
sane_lists | 一貫したリストのスタイルをサポート。 |
footnotes | 文書内の脚注のサポート。 |
次に、それぞれの拡張の使い方を説明します。
1. tables
Markdown のテーブル構文を使って、簡単にテーブルを作成できます。
| Header1 | Header2 |
| ------- | ------- |
| Row1C1 | Row1C2 |
| Row2C1 | Row2C2 |
2. nl2br
この拡張を有効にすると、Markdown の中での改行が、HTML の <br>
タグとして出力されます。
これがあるとTable内の改行とかもできるんで自分は便利だと感じました。
This is a line.
This is another line right after.
3. fenced_code
3つのバックティックを使用してコードブロックを作成できます。
```
This is a code block.
```
また、言語のハイライトを指定することも可能です。
ただ、codehiliteもですがそのままでは特にクラス名が入るだけなので色もつきません。
先ほどのQiitaの記事のように少し手を加えないとダメです。
コードのシンタックスハイライトのやり方はcodehiliteの部分で書きます。
```python
def hello():
print("Hello, World!")
```
4. codehilite
fenced_code
と組み合わせて使用すると、Pygments でのコードのハイライトが可能になります。
```python
def hello():
print("Hello, World!")
```
3, 4の補足「コードのシンタックスハイライトのやり方」
Pygments
というのプログラミングコードのシンタックスハイライトを行うためのライブラリを使用します。
Pygments
を使ってマークダウンの codehilite
拡張と組み合わせてシンタックスハイライトを行っていきます。
Pygments
のインストール
pip install Pygments
codehilite
拡張の使用
次に、マークダウンを変換するときに、codehilite
拡張を使って、
コードブロックのシンタックスハイライトを有効にします。
import markdown
md_text = """
```python
def hello():
print("Hello, World!")
```
"""
html_output = markdown.markdown(md_text, extensions=['fenced_code', 'codehilite'])
print(html_output)
md_text
に含まれるPythonコードブロックをシンタックスハイライトします。
CSSスタイルの適用
Pygments
はハイライトのスタイルを変更可能です。
好みのスタイルのCSSを生成して、HTMLページに適用することでできます。
例として、monokai
スタイルのCSSを生成する方法は以下です。
pygmentize -S monokai -f html -a .highlight > monokai.css
monokai.css
というファイルに monokai
スタイルのCSSを出力します。
そして、CSS内の全ての .highlight クラス名を .codehilite に変えます。
このCSSファイルをHTMLページにリンクすることで、シンタックスハイライトのスタイルを適用できます。
カラースキームの一覧は以下で見れます。
pygmentize -L styles
また、公式の Pygments
ウェブサイトにも、様々なスタイルのプレビューとともにスタイルの一覧が掲載されていて、
ウェブサイト上で具体的なコードとともにそれぞれのスタイルがどのように見えるかを確認可能みたいです。
Pygments official website の「Demo」セクションや「Styles gallery」見ることができます。
HTMLページに適用
生成した monokai.css
ファイルをHTMLページに組み込みます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Highlighted Code</title>
<link rel="stylesheet" href="monokai.css">
</head>
<body>
<!-- ここに markdown から変換された HTML 出力を挿入 -->
</body>
</html>
以上で、 Pygments
を使ったシンタックスハイライトを適用することができます。
5. wikilinks
Wiki のような内部リンクを作成することができます。
See [[PageName]] for more details.
6. toc
文書の冒頭や任意の位置に [TOC]
を追加すると、目次が自動生成されます。
これも以下のようにカスタマイズできるようになっています。
オプション | 説明 |
---|---|
title | 目次のタイトルを設定します。 |
baselevel | どのヘッダレベルから目次を開始するかを設定します。たとえば、baselevel=2 とすると、h2 からのヘッダが目次として考慮されます。 |
permalink | 各ヘッダにパーマリンク(アンカーリンク)を追加します。 |
slugify | ヘッダテキストからURLフレンドリーなスラッグを生成する方法をカスタマイズします。 |
以下がコード例です。
import markdown
from markdown.extensions.toc import TocExtension
text = """
# Title
## Subheading 1
### Sub-subheading 1
## Subheading 2
"""
# `toc` 拡張をカスタマイズして使用
toc_extension = TocExtension(title="Table of Contents", baselevel=2, permalink=True)
html = markdown.markdown(text, extensions=[toc_extension])
print(html)
7. sane_lists
sane_lists
という名前の拡張機能は、一般的なマークダウンパーサによく見られるリストの振る舞いの問題を修正するものです。
通常のマークダウンのパーサでは、リスト項目の番号は実際の値に関係なく、連続した数字を使用することが要求されることが多いです。
例えば
1. Item 1
1. Item 2
1. Item 3
上記のマークダウンは、以下のように正常に解析されます。
1. Item 1
2. Item 2
3. Item 3
しかし、中断がある場合、例えば
1. Item 1
3. Item 2
通常のマークダウンのパーサは、これを以下のように解析することがあります:
1. Item 1
2. Item 2
sane_lists
拡張を使用すると、この振る舞いが修正され、実際のリストの数字がそのまま使用されます。
この拡張を有効にすると、中断があるリストも正確に解析されます。
したがって、上記の例は次のようになります:
1. Item 1
3. Item 2
この拡張は、文書内でリストの順序が特定の意味や目的を持つ場合に特に役立ちます。
8. footnotes
文書の任意の位置に脚注を追加できます。
This is a statement[^1].
[^1]: This is the footnote.
2. テンプレートで使うためにタグを作成
ここは先ほどのQiitaの記事ほぼまんまですね。
変更ない部分はコピペにしています。
templatetags
ディレクトリを用意して、その中で自作フィルタを実装します.今回は例としてmyfilter.py
としています。
app/
__init__.py
models.py
templatetags/
__init__.py
myfilter.py
views.py
Qiitaの記事には書かれていませんでしたが、settings.py
にも変更が必要です。
DIRSの部分ですね。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
では、次にmyfilter.py
でカスタムタグを作成します。
先ほどのpythonのコードコピペですね。
from django import template
from django.template.defaultfilters import stringfilter
import markdown
from markdown.extensions.codehilite import CodeHiliteExtension
from markdown.extensions.toc import TocExtension
from markdown.extensions.sane_lists import SaneListExtension
from markdown.extensions.nl2br import Nl2BrExtension
from markdown.extensions.fenced_code import FencedCodeExtension
from markdown.extensions.wikilinks import WikiLinkExtension
from markdown.extensions.footnotes import FootnoteExtension
from markdown.extensions.tables import TableExtension
register = template.Library()
@register.filter
@stringfilter
def markdown2html(value):
extensions = [
CodeHiliteExtension(),
TocExtension(marker="[TOC]"),
SaneListExtension(),
Nl2BrExtension(),
FencedCodeExtension(),
WikiLinkExtension(base_url="/wiki/", end_url=".html"),
FootnoteExtension(),
TableExtension()
]
# Markdown を HTML に変換
return markdown.markdown(value, extensions=extensions)
3. テンプレートに埋め込み
最後にtemplates内で使得るようにします。
以下でカスタムタグを読み込むことができます。
{% load myfilter %}
次に以下でメソッドを呼び出します。
ここにあるsafe
はHTML
の自動エスケープへの対策です。
これがないとHTMLって認識してくれない程度の理解で大丈夫だと思います。
{{ markdowntxt | markdown2html | safe }}
終わりに
PythonでmarkdownをHTML変換することは出来ました。
あとはCSSなどで見た目を整えるだけですね。
また、コードブロックなど標準ではカスタマイズできない部分もあるのでそこらへんも考えないといけませんね。クリックでコピペとか。
Zennのようなものを作ろうとするとかなり大変なことがわかりました。
Noteのように直感的にわかりやすいエディタでHTML保存の方がもしかしたら良いのかもしれませんね。
いうてあれもマークダウン出保存してるかもだけど。
そこらへんは疎いのでわかりません。
ここまで長々と読んでいただきありがとうございました。
Discussion