PythonでHTMLをMarkdownに変換する:包括的なガイド

HTMLをクリーンでLLM対応のMarkdownに変換するためのPython

目次

HTMLをMarkdownに変換は、現代の開発ワークフローにおいて特にLLM(大規模言語モデル)、ドキュメンテーションシステム、Hugoなどの静的サイトジェネレータでウェブコンテンツを準備する際の基本的なタスクです。

HTMLは、豊富なスタイルと構造を持つウェブブラウザ向けに設計されていますが、Markdownはテキスト処理、バージョン管理、AIの消費に最適なクリーンで読みやすいフォーマットを提供します。Markdownの構文に初めて触れる場合は、Markdownチートシートを確認して、包括的な参考資料をご覧ください。

インフォグラフィック: HTMLからMarkdownへのページ変換

この包括的なレビューでは、HTMLからMarkdownへの変換に使用できる6つのPythonパッケージを紹介し、実用的なコード例、パフォーマンスベンチマーク、実際の使用例を提供します。LLMトレーニングパイプラインの構築、ブログのHugoへの移行、ドキュメンテーションのスクレイピングなど、ワークフローに最適なツールを見つけることができます。

代替アプローチ: 複雑なレイアウトのAI駆動型変換が必要な場合は、LLMとOllamaを使用してHTMLをMarkdownに変換も検討してください。

学ぶ内容:

  • 6つのライブラリの詳細な比較(それぞれの長所と短所)
  • 実際のHTMLサンプルを使用したパフォーマンスベンチマーク
  • 一般的な使用ケース向けの生産性のあるコード例
  • LLM前処理ワークフローのベストプラクティス
  • ご要望に応じた具体的な推奨事項

LLM前処理にMarkdownをなぜ使うのか?

ツールに深く潜る前に、なぜMarkdownがLLMワークフローにおいて特に価値があるのかを理解しましょう:

  1. トークン効率: 同じ内容ではHTMLよりもMarkdownがはるかに少ないトークンを使用します
  2. セマンティックな明確さ: Markdownは冗長なタグなしにドキュメント構造を保持します
  3. 読みやすさ: 人間とLLMの両方がMarkdownの構文を簡単に解析できます
  4. 一貫性: 標準化されたフォーマットにより、モデル入力の曖昧さが減少します
  5. ストレージ: トレーニングデータとコンテキストウィンドウのファイルサイズが小さくなります

Markdownの柔軟性はHTML変換にとどまらず、ドキュメンテーションワークフローではWordドキュメントをMarkdownに変換も可能で、知識管理システムではObsidianで個人知識管理も利用できます。

TL;DR - 一覧表の簡単な比較

急いでいる場合は、6つのライブラリの簡単な比較をご覧ください。この表は、ご要望に合ったツールを迅速に特定するのに役立ちます。

機能 html2text markdownify html-to-markdown trafilatura domscribe html2md
HTML5サポート 部分的 部分的 完全 完全 完全 完全
型ヒント なし なし あり 部分的 なし 部分的
カスタムハンドラ 限定的 優れた 良好 限定的 良好 限定的
テーブルサポート 基本 基本 高度 良好 良好 良好
非同期サポート なし なし なし なし なし あり
コンテンツ抽出 なし なし なし 優れた なし 良好
メタデータ抽出 なし なし あり 優れた なし あり
CLIツール なし なし あり あり なし あり
速度 非常に快 非常に快
アクティブな開発 なし あり あり あり 限定的 あり
Pythonバージョン 3.6+ 3.7+ 3.9+ 3.6+ 3.8+ 3.10+
依存関係 なし BS4 lxml lxml BS4 aiohttp

簡単な選択ガイド:

  • 速度が必要? → trafilatura または html2md
  • カスタマイズが必要? → markdownify
  • 型安全性が必要? → html-to-markdown
  • シンプルさが必要? → html2text
  • コンテンツ抽出が必要? → trafilatura

6つのPythonパッケージの比較: 主な候補者

それぞれのライブラリについて、実用的なコード例、設定オプション、実際のインサイトを深く掘り下げましょう。各セクションにはインストール手順、使用パターン、強みと限界の誠実な評価が含まれます。

1. html2text - 伝統的な選択肢

Aaron Swartzによって開発されたhtml2textは、10年以上にわたりPythonエコシステムで定番のツールです。クリーンで読みやすいMarkdown出力を生成することに焦点を当てています。

インストール:

pip install html2text

基本的な使用方法:

import html2text

# コンバータインスタンスの作成
h = html2text.HTML2Text()

# オプションの設定
h.ignore_links = False
h.ignore_images = False
h.ignore_emphasis = False
h.body_width = 0  # 行を折り返さない

html_content = """
<h1>ウェブスクレイピングへようこそ</h1>
<p>これは <strong>包括的なガイド</strong> です。</p>
<ul>
    <li>使いやすい</li>
    <li>実績あり</li>
    <li>広く採用されている</li>
</ul>
<a href="https://example.com">もっと見る</a>
"""

markdown = h.handle(html_content)
print(markdown)

出力:

# ウェブスクレイピングへようこそ

これは **包括的なガイド** です。

  * 使いやすい
  * 実績あり
  * 広く採用されている

[もっと見る](https://example.com)

高度な設定:

import html2text

h = html2text.HTML2Text()

# 特定の要素をスキップ
h.ignore_links = True
h.ignore_images = True

# フォーマットの制御
h.body_width = 80  # 80文字で折り返す
h.unicode_snob = True  # Unicode文字を使用
h.emphasis_mark = '*'  # 強調に*を使用
h.strong_mark = '**'

# テーブルの処理
h.ignore_tables = False

# プレフォーマットテキストの保護
h.protect_links = True

長所:

  • 成熟しており安定している(15年以上の開発歴)
  • 拡張可能な設定オプション
  • 端的なケースをよく処理
  • 外部依存なし

短所:

  • HTML5サポートが限定的
  • 一貫したスペースが不足する可能性
  • 活発なメンテナンスがされていない(2020年の最終更新)
  • 単一スレッド処理のみ

最適な用途: 簡単なHTMLドキュメント、レガシーシステム、安定性が最優先事項のとき


2. markdownify - 柔軟なオプション

markdownifyはBeautifulSoup4を使用して、カスタマイズ可能なタグ処理を提供します。

インストール:

pip install markdownify

基本的な使用方法:

from markdownify import markdownify as md

html = """
<article>
    <h2>現代のウェブ開発</h2>
    <p>Pythonと<code>現代のフレームワーク</code>を使用して構築。</p>
    <blockquote>
        <p>シンプルさは究極の洗練です。</p>
    </blockquote>
</article>
"""

markdown = md(html)
print(markdown)

出力:


## 現代のウェブ開発

Pythonと`現代のフレームワーク`を使用して構築。

> シンプルさは究極の洗練です。

カスタムハンドラを使用した高度な使用方法:

from markdownify import MarkdownConverter

class CustomConverter(MarkdownConverter):
    """
    特定のタグ処理を持つカスタムコンバータの作成
    """
    def convert_img(self, el, text, convert_as_inline):
        """カスタム画像ハンドラ(代替テキスト付き)"""
        alt = el.get('alt', '')
        src = el.get('src', '')
        title = el.get('title', '')

        if title:
            return f'![{alt}]({src} "{title}")'
        return f'![{alt}]({src})'

    def convert_pre(self, el, text, convert_as_inline):
        """コードブロックの処理を強化し、言語検出を実装"""
        code = el.find('code')
        if code:
            # クラス属性から言語を抽出(例: 'language-python')
            classes = code.get('class', [''])
            language = classes[0].replace('language-', '') if classes else ''
            return f'\n```{language}\n{code.get_text()}\n```\n'
        return f'\n```\n{text}\n```\n'

# カスタムコンバータの使用
html = '<pre><code class="language-python">def hello():\n    print("world")</code></pre>'
markdown = CustomConverter().convert(html)
print(markdown)

Markdownコードブロックとシンタックスハイライトの使用方法については、Markdownコードブロックの使用をご覧ください。

選択的なタグ変換:

from markdownify import markdownify as md

# 特定のタグを完全に削除
markdown = md(html, strip=['script', 'style', 'nav'])

# 特定のタグのみ変換
markdown = md(
    html,
    heading_style="ATX",  # ヘッディングに#を使用
    bullets="-",  # リストに-を使用
    strong_em_symbol="*",  # 強調に*を使用
)

長所:

  • BeautifulSoup4に基づいており(強力なHTML解析)
  • サブクラス化を通じて非常にカスタマイズ可能
  • 活発なメンテナンス
  • 良いドキュメンテーション

短所:

  • BeautifulSoup4依存が必要
  • 大規模なドキュメントでは遅くなる可能性
  • 建込のテーブルサポートが限定的

最適な用途: カスタム変換ロジック、BeautifulSoup4を使用しているプロジェクト


3. html-to-markdown - 現代的なパワフルなライブラリ

html-to-markdownは、包括的なHTML5サポートと拡張可能な設定オプションを持つ完全に型指定された現代的なライブラリです。

インストール:

pip install html-to-markdown

基本的な使用方法:

from html_to_markdown import convert

html = """
<article>
    <h1>技術ドキュメンテーション</h1>
    <table>
        <thead>
            <tr>
                <th>機能</th>
                <th>サポート</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>HTML5</td>
                <td>✓</td>
            </tr>
            <tr>
                <td>テーブル</td>
                <td>✓</td>
            </tr>
        </tbody>
    </table>
</article>
"""

markdown = convert(html)
print(markdown)

高度な設定:

from html_to_markdown import convert, Options

# カスタムオプションの作成
options = Options(
    heading_style="ATX",
    bullet_style="-",
    code_language_default="python",
    strip_tags=["script", "style"],
    escape_special_chars=True,
    table_style="pipe",  # テーブルに|を使用
    preserve_whitespace=False,
    extract_metadata=True,  # メタタグの抽出
)

markdown = convert(html, options=options)

コマンドラインインターフェース:

# 単一ファイルの変換
html-to-markdown input.html -o output.md

# オプション付きの変換
html-to-markdown input.html \
    --heading-style atx \
    --strip-tags script,style \
    --extract-metadata

# バッチ変換
find ./html_files -name "*.html" -exec html-to-markdown {} -o ./markdown_files/{}.md \;

長所:

  • HTML5サポート(セマンティック要素を含む)が完全
  • 標準的な型ヒント付きで、包括的な型指定
  • テーブル処理の強化(セルのマージ、整列)
  • メタデータ抽出機能
  • 活発な開発と現代的なコードベース

短所:

  • Python 3.9+が必要
  • 依存関係がやや大きい
  • 学習曲線が急

最適な用途: 複雑なHTML5ドキュメント、型指定されたプロジェクト、生産システム


4. trafilatura - コンテンツ抽出専門家

trafilaturaは単なるHTMLからMarkdownへのコンバータではなく、ウェブスクレイピングと記事抽出に特化したインテリジェントなコンテンツ抽出ライブラリです。

インストール:

pip install trafilatura

基本的な使用方法:

import trafilatura

# URLからダウンロードして抽出
url = "https://example.com/article"
downloaded = trafilatura.fetch_url(url)
markdown = trafilatura.extract(downloaded, output_format='markdown')
print(markdown)

注: TrafilaturaにはURLフェッチが組み込まれていますが、APIや認証されたエンドポイントでより複雑なHTTP操作が必要な場合は、cURLチートシートが役立ちます。

高度なコンテンツ抽出:

import trafilatura
from trafilatura.settings import use_config

# カスタム設定の作成
config = use_config()
config.set("DEFAULT", "EXTRACTION_TIMEOUT", "30")

html = """
<html>
<head><title>記事タイトル</title></head>
<body>
    <nav>ナビゲーションメニュー</nav>
    <article>
        <h1>主な記事</h1>
        <p>重要なコンテンツがここにあります。</p>
    </article>
    <aside>広告</aside>
    <footer>フッターコンテンツ</footer>
</body>
</html>
"""

# 本体コンテンツのみ抽出
markdown = trafilatura.extract(
    html,
    output_format='markdown',
    include_comments=False,
    include_tables=True,
    include_images=True,
    include_links=True,
    config=config
)

# メタデータ付きで抽出
result = trafilatura.extract(
    html,
    output_format='markdown',
    with_metadata=True
)

if result:
    print(f"タイトル: {result.get('title', 'N/A')}")
    print(f"著者: {result.get('author', 'N/A')}")
    print(f"日付: {result.get('date', 'N/A')}")
    print(f"\nコンテンツ:\n{result.get('text', '')}")

バッチ処理:

import trafilatura
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path

def process_url(url):
    """URLからMarkdownを抽出"""
    downloaded = trafilatura.fetch_url(url)
    if downloaded:
        return trafilatura.extract(
            downloaded,
            output_format='markdown',
            include_links=True,
            include_images=True
        )
    return None

# 複数のURLを並列処理
urls = [
    "https://example.com/article1",
    "https://example.com/article2",
    "https://example.com/article3",
]

with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(process_url, urls))

for i, markdown in enumerate(results):
    if markdown:
        Path(f"article_{i}.md").write_text(markdown, encoding='utf-8')

長所:

  • インテリジェントなコンテンツ抽出(不要な部分を削除)
  • 内蔵のURLフェッチと堅牢なエラーハンドリング
  • メタデータ抽出(タイトル、著者、日付)
  • 言語検出
  • ニュース記事やブログ投稿に最適化
  • Cベースのパーサーで高速

短所:

  • 一般的なHTMLでは過剰にコンテンツを削除する可能性
  • 記事抽出に特化(汎用的ではない)
  • エッジケースの設定複雑さ

最適な用途: ウェブスクレイピング、記事抽出、LLMトレーニングデータの準備


5. domscribe - セマンティックな保存者

domscribeは、HTMLからMarkdownへの変換中にセマンティックな意味を保持することに焦点を当てています。

インストール:

pip install domscribe

基本的な使用方法:

from domscribe import html_to_markdown

html = """
<article>
    <header>
        <h1>セマンティックHTMLの理解</h1>
        <time datetime="2024-10-24">2024年10月24日</time>
    </header>
    <section>
        <h2>導入</h2>
        <p>セマンティックHTMLは<mark>意味</mark>をコンテンツに提供します。</p>
    </section>
    <aside>
        <h3>関連トピック</h3>
        <ul>
            <li>アクセシビリティ</li>
            <li>SEO</li>
        </ul>
    </aside>
</article>
"""

markdown = html_to_markdown(html)
print(markdown)

カスタムオプション:

from domscribe import html_to_markdown, MarkdownOptions

options = MarkdownOptions(
    preserve_semantic_structure=True,
    include_aria_labels=True,
    strip_empty_elements=True
)

markdown = html_to_markdown(html, options=options)

長所:

  • セマンティックHTML5構造を保持
  • 現代のウェブコンポーネントをよく処理
  • クリーンなAPI設計

短所:

  • 開発初期段階(APIが変更される可能性)
  • 成熟した代替品と比較してドキュメンテーションが限定的
  • コミュニティが小さく、例が少ない

最適な用途: セマンティックHTML5ドキュメント、アクセシビリティに焦点を当てたプロジェクト、セマンティックHTML5構造の保持が重要な場合

注: domscribeは他のツールが優先しない特定のニッチを埋めていますが、まだ成熟した代替品ほど実績がありません。


6. html2md - 非同期パワフルなライブラリ

html2mdは、非同期処理を使用して高パフォーマンスのバッチ変換を設計しています。

インストール:

pip install html2md

コマンドラインでの使用:

# 全ディレクトリの変換
m1f-html2md convert ./website -o ./docs

# カスタム設定付き
m1f-html2md convert ./website -o ./docs \
    --remove-tags nav,footer \
    --heading-offset 1 \
    --detect-language

# 単一ファイルの変換
m1f-html2md convert index.html -o readme.md

プログラマティックな使用:

import asyncio
from html2md import convert_html

async def convert_files():
    """非同期バッチ変換"""
    html_files = [
        'page1.html',
        'page2.html',
        'page3.html'
    ]

    tasks = [convert_html(file) for file in html_files]
    results = await asyncio.gather(*tasks)
    return results

# 変換の実行
results = asyncio.run(convert_files())

長所:

  • 非同期処理による高パフォーマンス
  • インテリジェントなコンテンツセレクタ検出
  • YAMLフロントマター生成(Hugoに最適!)
  • コード言語検出
  • 並列処理サポート

短所:

  • Python 3.10+が必要
  • CLIに焦点を当てている(柔軟なAPIが少ない)
  • ドキュメンテーションがより包括的であるべき

最適な用途: 大規模な移行、バッチ変換、Hugo/Jekyll移行


パフォーマンスベンチマーク

パフォーマンスは、LLMトレーニングや大規模な移行で数千のドキュメントを処理する際特に重要です。ライブラリ間の相対的な速度の違いを理解することで、ワークフローに最適な選択をできます。

比較的なパフォーマンス分析:

一般的な使用パターンに基づき、これらのライブラリが3つの現実的なシナリオでどのように比較されるかを示します:

  1. 単純なHTML: テキスト、見出し、リンクを含む基本的なブログ投稿(5KB)
  2. 複雑なHTML: ネストされたテーブルとコードブロックを含む技術ドキュメンテーション(50KB)
  3. 実際のウェブサイト: ナビゲーション、フッター、サイドバー、広告を含むフルウェブページ(200KB)

以下は、これらのライブラリを自分でテストするための例のベンチマークコードです:

import time
import html2text
from markdownify import markdownify
from html_to_markdown import convert
import trafilatura

def benchmark(html_content, iterations=100):
    """変換速度のベンチマーク"""

    # html2text
    start = time.time()
    h = html2text.HTML2Text()
    for _ in range(iterations):
        _ = h.handle(html_content)
    html2text_time = time.time() - start

    # markdownify
    start = time.time()
    for _ in range(iterations):
        _ = markdownify(html_content)
    markdownify_time = time.time() - start

    # html-to-markdown
    start = time.time()
    for _ in range(iterations):
        _ = convert(html_content)
    html_to_markdown_time = time.time() - start

    # trafilatura
    start = time.time()
    for _ in range(iterations):
        _ = trafilatura.extract(html_content, output_format='markdown')
    trafilatura_time = time.time() - start

    return {
        'html2text': html2text_time,
        'markdownify': markdownify_time,
        'html-to-markdown': html_to_markdown_time,
        'trafilatura': trafilatura_time
    }

典型的なパフォーマンス特性(代表的な相対的な速度):

パッケージ 単純(5KB) 複雑(50KB) 実際のサイト(200KB)
html2text
markdownify 最も遅い
html-to-markdown
trafilatura 非常に快 非常に快
html2md(非同期) 非常に快 非常に快 最速

主な観察:

  • html2mdtrafilaturaは複雑なドキュメントで最も速く、バッチ処理に最適です
  • html-to-markdownは生産性のための速度と機能のバランスが最も良い
  • markdownifyは遅くても最も柔軟で、カスタムハンドラが必要な場合に妥協が価値があります
  • html2textは年齢のため遅く、しかし単純なケースでは安定しています

注: パフォーマンスの違いは、数百または数千のファイルを処理するときにのみ顕著になります。偶発的な変換では、どのライブラリでも問題ありません。機能とカスタマイズオプションに注目してください。

実際の使用例

理論は役立ちますが、実際の例はこれらのツールが本番環境でどのように機能するかを示します。以下に、独自のプロジェクトに適応できる本番環境で使用できる4つの一般的なシナリオを紹介します。

使用例1: LLMトレーニングデータの準備

要件: 千数百のドキュメンテーションページからクリーンなテキストを抽出

推奨: trafilatura + 並列処理

import trafilatura
from pathlib import Path
from concurrent.futures import ProcessPoolExecutor

def process_html_file(html_path):
    """HTMLファイルをマークダウンに変換"""
    html = Path(html_path).read_text(encoding='utf-8')
    markdown = trafilatura.extract(
        html,
        output_format='markdown',
        include_links=False,  # よりクリーンなトレーニングデータのために削除
        include_images=False,
        include_comments=False
    )

    if markdown:
        output_path = html_path.replace('.html', '.md')
        Path(output_path).write_text(markdown, encoding='utf-8')
        return len(markdown)
    return 0

# 10,000ファイルを並列処理
html_files = list(Path('./docs').rglob('*.html'))

with ProcessPoolExecutor(max_workers=8) as executor:
    token_counts = list(executor.map(process_html_file, html_files))

print(f"{len(html_files)}ファイルを処理しました")
print(f"合計文字数: {sum(token_counts):,}")

使用例2: Hugoブログの移行

要件: WordPressブログをHugoにフロントマター付きで移行

推奨: html2md CLI

Hugoは人気のあるスタティックサイトジェネレータで、コンテンツにMarkdownを使用します。Hugoに特化したヒントについては、Hugoチートシートを確認し、Hugoサイトに構造化データマークアップを追加する方法について学び、SEOを改善してください。

# フロントマター付きですべての投稿を変換
m1f-html2md convert ./wordpress-export \
    -o ./hugo/content/posts \
    --generate-frontmatter \
    --heading-offset 0 \
    --remove-tags script,style,nav,footer

またはプログラム的に:

from html_to_markdown import convert, Options
from pathlib import Path
import yaml

def migrate_post(html_file):
    """WordPress HTMLをHugoマークダウンに変換"""
    html = Path(html_file).read_text()

    # HTMLからタイトルと日付を抽出
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html, 'html.parser')
    title = soup.find('h1').get_text() if soup.find('h1') else 'Untitled'

    # マークダウンに変換
    options = Options(strip_tags=['script', 'style', 'nav', 'footer'])
    markdown = convert(html, options=options)

    # Hugoフロントマターを追加
    frontmatter = {
        'title': title,
        'date': '2024-10-24',
        'draft': False,
        'tags': []
    }

    output = f"---\n{yaml.dump(frontmatter)}---\n\n{markdown}"

    # 保存
    output_file = html_file.replace('.html', '.md')
    Path(output_file).write_text(output, encoding='utf-8')

# すべての投稿を処理
for html_file in Path('./wordpress-export').glob('*.html'):
    migrate_post(html_file)

使用例3: カスタムフォーマット付きドキュメンテーションスクレイパー

要件: カスタムコードブロック処理付きで技術ドキュメントをスクレイプ

推奨: markdownifyとカスタムコンバーター

このアプローチは、wikiシステムからドキュメンテーションを移行する際に特に役立ちます。ドキュメンテーションを管理している場合、DokuWiki - 自己ホスト型のwikiと代替案に興味があるかもしれません。

from markdownify import MarkdownConverter
import requests

class DocsConverter(MarkdownConverter):
    """技術ドキュメンテーション用のカスタムコンバーター"""

    def convert_pre(self, el, text, convert_as_inline):
        """シンタックスハイライト付きのコードブロック"""
        code = el.find('code')
        if code:
            # クラスから言語を抽出
            classes = code.get('class', [])
            language = next(
                (c.replace('language-', '') for c in classes if c.startswith('language-')),
                'text'
            )
            return f'\n```{language}\n{code.get_text()}\n```\n'
        return super().convert_pre(el, text, convert_as_inline)

    def convert_div(self, el, text, convert_as_inline):
        """特殊なドキュメンテーションブロックの処理"""
        classes = el.get('class', [])

        # 警告ブロック
        if 'warning' in classes:
            return f'\n> ⚠️ **Warning**: {text}\n'

        # 情報ブロック
        if 'info' in classes or 'note' in classes:
            return f'\n> 💡 **Note**: {text}\n'

        return text

def scrape_docs(url):
    """ドキュメンテーションページをスクレイプして変換"""
    response = requests.get(url)
    markdown = DocsConverter().convert(response.text)
    return markdown

# 使用
docs_url = "https://docs.example.com/api-reference"
markdown = scrape_docs(docs_url)
Path('api-reference.md').write_text(markdown)

使用例4: ニュースレターをマークダウンアーカイブに変換

要件: HTMLメールニュースレターを読みやすいマークダウンに変換

推奨: 特定の設定でhtml2textを使用

import html2text
import email
from pathlib import Path

def convert_newsletter(email_file):
    """HTMLメールをマークダウンに変換"""
    # メールを解析
    with open(email_file, 'r') as f:
        msg = email.message_from_file(f)

    # HTML部分を取得
    html_content = None
    for part in msg.walk():
        if part.get_content_type() == 'text/html':
            html_content = part.get_payload(decode=True).decode('utf-8')
            break

    if not html_content:
        return None

    # コンバーターを設定
    h = html2text.HTML2Text()
    h.ignore_images = False
    h.images_to_alt = True
    h.body_width = 0
    h.protect_links = True
    h.unicode_snob = True

    # 変換
    markdown = h.handle(html_content)

    # メタデータを追加
    subject = msg.get('Subject', 'No Subject')
    date = msg.get('Date', '')

    output = f"# {subject}\n\n*Date: {date}*\n\n---\n\n{markdown}"

    return output

# ニュースレターのアーカイブを処理
for email_file in Path('./newsletters').glob('*.eml'):
    markdown = convert_newsletter(email_file)
    if markdown:
        output_file = email_file.with_suffix('.md')
        output_file.write_text(markdown, encoding='utf-8')

シナリオ別の推奨事項

どのライブラリを選ぶべきかまだ迷っている場合は、特定の使用例に基づいた私の決定的なガイドをご覧ください。これらの推奨事項は、各ライブラリを本番環境で実際に使用した経験に基づいています。

WebスクレイピングとLLM前処理向け

最適: trafilatura

Trafilaturaは、不要な要素を除去しながらクリーンなコンテンツを抽出する点で優れています。以下のような用途に最適です:

  • LLMトレーニングデータセットの構築
  • コンテンツの集約
  • 研究論文の収集
  • ニュース記事の抽出

Hugo/Jekyll移行向け

最適: html2md

非同期処理とフロントマター生成により、大量の移行が迅速かつ簡単に実行できます:

  • バッチ変換
  • 自動メタデータ抽出
  • YAMLフロントマター生成
  • タイトルレベルの調整

カスタム変換ロジック向け

最適: markdownify

コンバーターをサブクラス化して完全な制御を実現できます:

  • カスタムタグハンドラ
  • ドメイン固有の変換
  • 特殊なフォーマット要件
  • 既存のBeautifulSoupコードとの統合

型安全な本番システム向け

最適: html-to-markdown

現代的で、型安全かつ機能が豊富です:

  • 完全なHTML5サポート
  • 総合的な型ヒント
  • 高度なテーブル処理
  • 活発なメンテナンス

簡単で安定した変換向け

最適: html2text

「ただ動作する」ことを必要とする場合に最適です:

  • 依存関係なし
  • 戦闘済み
  • 拡張可能な設定
  • 幅広いプラットフォームサポート

LLM前処理のベストプラクティス

どのライブラリを選択しても、以下のベストプラクティスに従うことで、LLM消費に最適化された高品質なマークダウン出力が得られます。これらのパターンは、数百万のドキュメントを処理する本番ワークフローで必須とされています。

1. 変換前にクリーンアップ

変換前に不要な要素を削除することで、よりクリーンな出力とより良いパフォーマンスが得られます:

from bs4 import BeautifulSoup
import trafilatura

def clean_and_convert(html):
    """変換前に不要な要素を削除"""
    soup = BeautifulSoup(html, 'html.parser')

    # 不要な要素を削除
    for element in soup(['script', 'style', 'nav', 'footer', 'header', 'aside']):
        element.decompose()

    # 広告とトラッキングを削除
    for element in soup.find_all(class_=['ad', 'advertisement', 'tracking']):
        element.decompose()

    # クリーンなHTMLを変換
    markdown = trafilatura.extract(
        str(soup),
        output_format='markdown'
    )

    return markdown

2. ホワイトスペースを正規化

さまざまなコンバーターがホワイトスペースを異なる方法で処理します。出力を一貫性を持たせるために正規化してください:

import re

def normalize_markdown(markdown):
    """マークダウンのホワイトスペースを整える"""
    # 複数の空白行を削除
    markdown = re.sub(r'\n{3,}', '\n\n', markdown)

    # 末尾のホワイトスペースを削除
    markdown = '\n'.join(line.rstrip() for line in markdown.split('\n'))

    # 終端に1つの改行を保証
    markdown = markdown.rstrip() + '\n'

    return markdown

3. 出力を検証

品質管理は不可欠です。変換エラーを早期に検出するために検証を実装してください:

def validate_markdown(markdown):
    """マークダウンの品質を検証"""
    issues = []

    # HTMLの残りをチェック
    if '<' in markdown and '>' in markdown:
        issues.append("HTMLタグが検出されました")

    # 破損したリンクをチェック
    if '[' in markdown and ']()' in markdown:
        issues.append("空のリンクが検出されました")

    # 過剰なコードブロックをチェック
    code_block_count = markdown.count('```')
    if code_block_count % 2 != 0:
        issues.append("閉じられていないコードブロック")

    return len(issues) == 0, issues

4. バッチ処理テンプレート

大量のドキュメントコレクションを処理する際には、この本番環境で使用できるテンプレートを使用し、適切なエラーハンドリング、ロギング、並列処理を実装してください:

from pathlib import Path
from concurrent.futures import ProcessPoolExecutor
import trafilatura
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def process_file(html_path):
    """単一のHTMLファイルを処理"""
    try:
        html = Path(html_path).read_text(encoding='utf-8')
        markdown = trafilatura.extract(
            html,
            output_format='markdown',
            include_links=True,
            include_images=False
        )

        if markdown:
            # 正規化
            markdown = normalize_markdown(markdown)

            # 検証
            is_valid, issues = validate_markdown(markdown)
            if not is_valid:
                logger.warning(f"{html_path}: {', '.join(issues)}")

            # 保存
            output_path = Path(str(html_path).replace('.html', '.md'))
            output_path.write_text(markdown, encoding='utf-8')

            return True

        return False

    except Exception as e:
        logger.error(f"{html_path}の処理中にエラーが発生しました: {e}")
        return False

def batch_convert(input_dir, max_workers=4):
    """ディレクトリ内のすべてのHTMLファイルを変換"""
    html_files = list(Path(input_dir).rglob('*.html'))
    logger.info(f"{len(html_files)}個のHTMLファイルが見つかりました")

    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        results = list(executor.map(process_file, html_files))

    success_count = sum(results)
    logger.info(f"{success_count}/{len(html_files)}ファイルが成功裏に変換されました")

# 使用
batch_convert('./html_docs', max_workers=8)

結論

Pythonエコシステムには、HTMLからMarkdownへの変換に最適化された成熟した本番環境で使用できるツールが豊富に存在します。 選択は、あなたの具体的な要件に合わせるべきです:

  • 迅速な変換: html2textを使用し、そのシンプルさとゼロ依存性を活用してください
  • カスタムロジック: markdownifyを使用し、サブクラス化を通じて最大の柔軟性を実現してください
  • Webスクレイピング: trafilaturaを使用し、ナビゲーション、広告、不要な要素を除去しながらスマートなコンテンツ抽出を実現してください
  • 大規模な移行: html2mdを使用し、大規模なプロジェクトで非同期パフォーマンスを実現してください
  • 本番システム: html-to-markdownを使用し、型安全性と包括的なHTML5サポートを活用してください
  • 意味の保持: domscribeを使用し、HTML5の意味構造を保持してください

LLMワークフロー向けの推奨事項

LLM前処理ワークフローでは、2段階のアプローチが推奨されます:

  1. trafilaturaから開始し、初期のコンテンツ抽出を行ってください。これはナビゲーション、広告、不要な要素をスマートに除去しながらメインコンテンツを保持します
  2. html-to-markdownにフォールバックし、テーブルやコードブロックを含む技術ドキュメントなどの正確な構造保持が必要な複雑なドキュメントに適しています

この組み合わせは、95%の現実的なシナリオを効果的に処理します。

次のステップ

これらのツール(html2textを除く)はすべて活発にメンテナンスされており、本番環境で使用可能です。以下の手順を実行することをお勧めします:

  1. ご使用のユースケースに合ったライブラリを2〜3つインストールしてください
  2. 実際のHTMLサンプルでそれらをテストしてください
  3. ごく典型的なドキュメントサイズでパフォーマンスをベンチマークしてください
  4. 出力品質に基づいて選択してください、速度だけではなく

PythonエコシステムにおけるHTMLからMarkdownへの変換は、大幅に成熟しており、それぞれの目的に応じてこれらの選択肢のいずれかを選択しても間違いありません。

その他のリソース

注意: この比較は、公式ドキュメント、コミュニティのフィードバック、ライブラリのアーキテクチャの分析に基づいています。パフォーマンス特性は、典型的な使用パターンを代表しています。特定のユースケースについては、実際のHTMLサンプルを使用して独自のベンチマークを実行してください。

その他の役に立つ記事