概要

python の http.server.SimpleHTTPRequestHandler を使って Markdown をHTMLに変換する簡単なサーバを書いてみた。
pythonモジュールの markdown を利用したが、コードブロックの対応がいまいちだったり、スタイルを足したかったりしたので一部実装を追加した。
※markdown2 も試してみたが、しっくりくる変換がされなかった。

目次

環境作成

mkdir markdown_server
cd markdown_server
python3 -m venv venv
source venv/bin/activate
pip install markdown

サーバを書く

https://docs.python.org/ja/3/library/http.server.html
https://docs.python.org/ja/3/library/http.server.html#http.server.SimpleHTTPRequestHandler

server.py

"""
Markdown公開用サーバ.

拡張子.mdのコンテンツがリクエストされた時は Markdown をHTMLに変換して返却する.
"""

from http import HTTPStatus
import http.server
import io
import markdown
import os
import re
import shutil
import socketserver


PORT = 8000

MARKDOWN_STYLE = """
<style>
.container {
    max-width: 920px;
    margin: 0 auto 20px auto;
}
.container h1 {
    padding-bottom: 0.3em;
    font-size: 2.25em;
    line-height: 1.2;
    border-bottom: 1px solid #eee;
}
.container h2 {
    padding-bottom: 0.3em;
    font-size: 1.75em;
    line-height: 1.225;
    border-bottom: 1px solid #eee;
    margin-top: 1em;
    margin-bottom: 16px;
    font-weight: bold;
}
blockquote {
    margin-left: 0;
    margin-right: 0;
    padding-left: 10px;
    border-left: 4px solid #999;
}
blockquote p {
    margin: 2px 0;
}
.code {
    margin: 0;
    padding: 0.5rem 1rem;
    background: #f0f0f0;
}
</style>
"""

MARKDOWN_HTML = f"""
<!doctype html>
<html>
<head>
{MARKDOWN_STYLE}
<meta charset="utf-8">
</head>
<body>
<div class='container'>
{{MARKDOWN_CONTENTS}}
</div>
</body>
</html>
"""


class MarkdownHandler(http.server.SimpleHTTPRequestHandler):
    """
    Markdown対応のリクエストハンドラ.
    """

    def do_GET(self):
        """
        GETリクエストのハンドリングを行う.

        ・拡張子 .md がリクエストされた場合は内容をHTMLに変換して返却する.
        ・Markdownのhtml変換は基本的に markdown により行う.
        ・コードブロック( ``` 〜 ``` )のHTML変換は独自処理.
        """
        #base, ext = posixpath.splitext(self.path)
        path = self.path.split("?")[0][1:]
        *_, ext = path.split(".")
        if ext == "md" and os.path.exists(path):
            with open(path, "r") as f:
                md_text = "".join(f.readlines())
            md_body = self.md2html(md_text)
            contents = MARKDOWN_HTML
            #contents = contents.replace("{MARKDOWN_STYLE}", MARKDOWN_STYLE)
            contents = MARKDOWN_HTML.replace("{MARKDOWN_CONTENTS}", md_body)
            response = contents.encode("utf-8")
            self.send_response(HTTPStatus.OK)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.send_header("Content-Length", len(response))
            self.send_header("Cache-Control" , "no-store")
            self.send_header("Pragma"        , "no-cache")
            self.end_headers()
            shutil.copyfileobj(io.BytesIO(response), self.wfile)
            return
        super().do_GET()

    def md2html(self, text):
        """
        markdownのhtml変換.

        MarkdownテキストをHTMLに変換する.
        """
        text = self.convert_code_block(text)
        return markdown.Markdown().convert(text)

    def convert_code_block(self, text):
        """
        コードブロックの変換.

        ``` 〜 ``` を<pre class="code">...</pre>に変換する。
        """
        lines = text.split("\n")
        targets   = [i for i,x in enumerate(lines) if re.match("^```([a-zA-Z]*)$", x)]
        for i,no in enumerate(targets):
            if i % 2 == 0:  # 開始と終了で1セットと考える
                lines[no+1] = "<pre class='code'>" + lines[no+1]
                lines[no] = ""
                no = targets[i+1]  # TODO: エラーハンドリング(終了タグがない場合)
                lines[no-1] = lines[no-1] + "</pre>"
                lines[no] = ""
        return "\n".join(lines)
        
if __name__ == '__main__':

    # ポートの再利用を許可する(コレがないとしばらくの間は同じポートを利用できない)
    socketserver.TCPServer.allow_reuse_address = True

    with socketserver.TCPServer(("", PORT), MarkdownHandler) as httpd:
        print("serving at port", PORT)
        httpd.serve_forever()

起動

python3 server.py

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2019-12-18 (水) 22:25:05 (1589d)