- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2019-12-18T11:52:58+00:00","","")
#mynavi(Python覚え書き)
#setlinebreak(on);
* 概要 [#y772906e]
#html(<div class="pl10">)
python の http.server.SimpleHTTPRequestHandler を使って Markdown をHTMLに変換する簡単なサーバを書いてみた。
pythonモジュールの markdown を利用したが、コードブロックの対応がいまいちだったり、スタイルを足したかったりしたので一部実装を追加した。
※markdown2 も試してみたが、しっくりくる変換がされなかった。
#html(</div>)
* 目次 [#o25c6c15]
#contents
- 関連
-- [[Python覚え書き]]
-- [[pythonでWebサーバを書いてみる]]
* 環境作成 [#f3d33b1c]
#html(<div class="pl10">)
#myterm2(){{
mkdir markdown_server
cd markdown_server
python3 -m venv venv
source venv/bin/activate
pip install markdown
}}
#html(</div>)
* サーバを書く [#j3ccc9ba]
#html(<div class="pl10">)
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
#mycode2(python){{
"""
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()
}}
#html(</div>)
* 起動 [#k76adbd3]
#html(<div class="pl10">)
#myterm2(){{
python3 server.py
}}
#html(</div>)