備忘録です。
Word文書をマークダウン形式ファイルに変換するスクリプトを作成したのでメモです。
import os
from docx import Document
from docx.enum.text import WD_COLOR_INDEX
def process_paragraph(paragraph):
"""
1つの段落の中身(Run)を調べて、太字やコードをマークダウン記号に戻すよ。
"""
md_line = ""
for run in paragraph.runs:
text = run.text
if not text:
continue
# 1. コードの判定(背景が黒、またはフォントがCourier Newなら ` で囲むよ)
if run.font.highlight_color == WD_COLOR_INDEX.BLACK or run.font.name == 'Courier New':
text = f"`{text}`"
# 2. 太字の判定(太字属性があれば ** で囲むよ)
elif run.bold:
text = f"**{text}**"
md_line += text
return md_line
def convert_docx_to_md(docx_filepath, output_md_path):
"""
Wordファイルを読み込んで、マークダウン形式のテキストとして保存するよ。
"""
if not os.path.exists(docx_filepath):
print(f"エラー: {docx_filepath} が見つからないよ。")
return
doc = Document(docx_filepath)
md_content = []
# Wordの中身を1つずつチェックしていくよ
for element in doc.element.body:
# 段落(Paragraph)の場合
if element.tag.endswith('p'):
from docx.text.paragraph import Paragraph
para = Paragraph(element, doc)
style_name = para.style.name
text = para.text.strip()
if not text:
md_content.append("")
continue
# 見出しの逆変換
if style_name == 'Heading 1':
md_content.append(f"# {process_paragraph(para)}")
elif style_name == 'Heading 2':
md_content.append(f"## {process_paragraph(para)}")
elif style_name == 'Heading 3':
md_content.append(f"### {process_paragraph(para)}")
# リストの逆変換(「・」や「1. 」で始まっているかチェック)
elif text.startswith('・'):
content = process_paragraph(para)[1:].strip()
md_content.append(f"- {content}")
elif text[0].isdigit() and text[1:3] == ". ":
# 「1. 」などの数字部分をそのまま活かして変換するよ
md_content.append(process_paragraph(para))
# 普通の文章
else:
md_content.append(process_paragraph(para))
# 表(Table)の場合
elif element.tag.endswith('tbl'):
from docx.table import Table
table = Table(element, doc)
for i, row in enumerate(table.rows):
cells = [cell.text.strip() for cell in row.cells]
row_text = "| " + " | ".join(cells) + " |"
md_content.append(row_text)
# ヘッダーの下に区切り線を入れるよ
if i == 0:
separator = "| " + " | ".join(["---"] * len(cells)) + " |"
md_content.append(separator)
# ファイルに書き出すよ
with open(output_md_path, 'w', encoding='utf-8') as f:
f.write("\n\n".join(md_content))
print(f"変換完了!: {output_md_path}")
if __name__ == "__main__":
# --- ここを自分のファイル名に合わせてね ---
filename = "../../放課後プログラミング〜月島夏鈴と学ぶ はじめてのPython〜"
input_docx = f"{filename}.docx"
output_md = f"{filename}.md"
try:
convert_docx_to_md(input_docx, output_md)
except Exception as e:
print(f"エラーが発生しちゃった:{e}")
このスクリプトは、Word(.docx)の体裁をできる範囲でMarkdown記法へ“逆変換”するツールです。主役は2つの関数で、段落内の装飾をRun単位でMarkdownに落とし込み(process_paragraph)、文書全体を段落・表ごとに走査してMarkdownテキストを組み立てます(convert_docx_to_md)。
まず process_paragraph(paragraph) は、段落内の Run を左から順に取り出し、次の優先順位で置換します。
1. 「背景ハイライトが黒」または「フォント名が Courier New」の Run はインラインコード扱いとしてバッククォート(`…`)で囲む。
2. それ以外で太字フラグ(run.bold)が立っているRunは **…** で囲む。
3. 条件に当てはまらないテキストはそのまま連結。
空テキストのRunは無視し、結果として段落1行ぶんのMarkdown文字列を返します。if / elif になっているため、「コード認定」が太字よりも先に適用されるのがポイントです。
convert_docx_to_md(docx_filepath, output_md_path) は、文書全体をMarkdownへ組み立てる関数です。最初に入力ファイルの存在確認をし、python-docx の Document で読み込みます。以降は doc.element.body(内部XMLの子要素)を直接たどり、段落か表かで処理を分けます。
• 段落(…endswith(‘p’))の場合は Paragraph を生成し、段落スタイルとテキストを取得。
• スタイル名が Heading 1/2/3 のときは、それぞれ # / ## / ### を先頭に付け、見出し行として process_paragraph の結果を差し込みます。
• 先頭が全角中点「・」なら、Markdownの箇条書き – に置き換えます(※見た目の「・」を1文字取り除いてから出力)。
• 先頭が「1. 」のように 1桁数字 + ドット + 半角スペース の形なら、そのまま番号付きリストの行として採用します(多桁には未対応)。
• それ以外は通常段落として process_paragraph の結果を追加します。
段落テキストが空なら空行を入れて段落間の区切りにします。
• 表(…endswith(‘tbl’))は、各行を | セル1 | セル2 | … | 形式に整形。1行目(ヘッダー)の直後に、列数に応じた区切り線 | — | — | … | を自動で挿入します。
最後に、収集したMarkdown行を空行(\n\n)で結合し、UTF-8で .md ファイルへ書き出します。エントリポイントでは入力・出力パスを組み立てて関数を呼び出し、成功時は完了メッセージ、例外時は日本語のエラーメッセージを表示します。
設計上の要点は「Word特有の装飾を単純な規則でMarkdownへ写し取る」ことにあります。Run単位でのコード/太字の判定、段落スタイルからの見出し復元、簡易的な箇条書き・番号付きリストの検出、そして表のパイプ区切り出力までを、最小限のロジックでカバーしています。
コメント