空飛ぶ気まぐれ雑記帳

主に趣味とかプログラミングについて扱います。

PythonでMarkdownをHTMLに変換する

4月になってから環境が変わってクソ忙しくなったせいか、録にブログを更新できず。
なんだかんだで、もう5月も終わり。プログラムを書いてないわけじゃないけど、githubの履歴を見る限り一ヶ月でたったの6000行しか書いてないので、全然ですね。
それはさておき、流石に月間更新数0はいただけないので、何か書こうと思っていたら、ネタはあったので投下します。

# なぜにPythonで?
ただMarkdownを変換するだけならpandocなりを使えば容易に実現可能なのですが、table要素にはclassを追加して、相対パスの画像をサーバにアップロードしたURLに変換してという作業を自動化するのはそう簡単ではありません。
それでペチペチとGoogle先生にご教示願ったら、それっぽいのがちゃんとあったんですよね。


Python Markdown

# 見付けたは良いが…
ただ、コイツ。ドキュメントを読んでもさっぱり使い方がわからない。
いや、少なくとも、Markdown->HTMLに変換する方法はよく分かるのだけれども、それ以上のことをしようとすると何をする必要があるのかさっぱりわからない。
実際Githubのissueを見ると山のような質問。あっ察しというわけで、再びGoogle先生にご教示願うと無事それっぽいのを見つけることができました。
ana-balica.github.io

一通りの使い方はなんとなく書いてあったので、実装してみました。
以下が実際のコード

#encoding:UTF-8

import sys
import glob
import os
import os.path
import markdown
import codecs
from markdown import Extension
from markdown.util import etree
from markdown.treeprocessors import Treeprocessor
from markdown.extensions import tables, toc

def main(argv):
  if len(argv) < 3:
    print('convert.py <Input directory> <output directory>')
    return

  md = markdown.Markdown(extensions = [ElementTreeExtension(), 'markdown.extensions.tables', 'markdown.extensions.toc'])

  input_dir = argv[1]
  output_dir = argv[2]

  try:
    os.mkdir(output_dir)
  except OSError as e:
    if not os.path.exists(output_dir):
      rethrow()

  for file in glob.iglob(os.path.join(input_dir, "**/*.md"), recursive=True):
    print(file)
    output_filename = get_output_filename(file, input_dir, output_dir)
    with codecs.open(file, 'r', 'UTF-8') as fp, codecs.open(output_filename, 'w+', 'UTF-8') as ofp :    
      text = fp.read()
      markdown_text = md.convert(text)
      ofp.write(markdown_text)

class ElementTreeExtension(Extension):
  def extendMarkdown(self, md, md_globals):
    md.treeprocessors.add('elementtreeprocessor', ElementTreeProcessor(), '_end')
    md.registerExtension(self)
class ElementTreeProcessor(Treeprocessor):
  def run(self, root):
    h1s = root.getiterator("h1")
    for h1 in h1s:
      h1.set("class", "h")
    
def get_output_filename(input_filename, input_root_dir, output_dir):
  input_dir, filename = os.path.split(input_filename)
  base, ext = os.path.splitext(filename)
  relative_root_to_file = os.path.relpath(input_dir, input_root_dir)
  output_full_path = os.path.join(output_dir, relative_root_to_file)
  if not os.path.isdir(output_full_path) and not os.path.exists(output_full_path):
    os.mkdir(output_full_path)

  return os.path.join(output_full_path, base + ".html")

if __name__ == '__main__':
  main(sys.argv)

第1引数にMarkdown形式のファイルが保存されたディレクトリ。
第2引数に出力ファイルが保存されるディレクトリを設定すれば良い。
これで変換したhtmlのh1要素にhというclassが自動で追加されるようになっている。