概要 †python の http.server.SimpleHTTPRequestHandler を使って Markdown をHTMLに変換する簡単なサーバを書いてみた。 目次 †環境作成 †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 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 |