プログラミング

PDFをDoclingでMarkdown化する

Doclingは無料のオープンソースツールで、文書のレイアウトを解析して、表は表の形のまま、スキャン書類は文字を読み取って、Markdown形式のテキストにします。処理はすべて自分のパソコンの中で完結するので、社外に出せない書類にも使えます。

本記事ではDoclingの基本的な使い方を整理し、どのくらいまでPDFを正しく変換できるかを試しました。また、うまくいかなかったものに対して対処法があるものについては、その方法をまとめました。

Pythonの環境がまだの場合は「Python Install ManagerでのPython環境構築」や「uvのインストールと使い方」を先にご覧ください。

動作確認

Python 3.12/docling 2.113.0で確認しています。

本文の記述は2026年7月時点のものです。

目次

インストール

pythonにdoclingのパッケージをインストールします。

pip install docling

# uvを使用する場合
uv add docling

Doclingは内部で使うAI用のライブラリ(PyTorch)のサイズが大きく、環境によっては数GBになります。GPUを使わないのであれば、次のように軽量なCPU版をインストールするようにすると、サイズを1GB未満にすることができます。

pip install docling --extra-index-url https://download.pytorch.org/whl/cpu

# uvを使用する場合
uv add docling --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match

--extra-index-urlは指定したインデックスをPyPIに追加することを意味します。これにより指定したインデックスとPyPIの両方からライブラリをインストールできます。指定したインデックスからPyTorchをインストールしますが、doclingはPyPIにあるため、両方使えるようにします。

ライブラリをインストールした後、初めてDoclingを実行したときに、AIモデルのダウンロードが行われます。これには数分~数十分かかりますが、最初の一回だけになります。

AIモデルのダウンロード

Hugging Faceにアクセスするので、サイトにユーザー登録してトークンを発行した後、環境変数HF_TOKENにトークンを設定しておくとダウンロードが安定します。

なお、このダウンロードはDoclingを初めて使った場合にのみ行われます。uvを使った場合は、uvのプロジェクトフォルダとは別のところに保存されるため、プロジェクトを作り直しても再ダウンロードはされません。

基本的な使い方

Markdownファイルに変換する(最小構成)

ターミナルからコマンド1つでMarkdownファイルができます。

docling あなたのファイル.pdf

# uvの場合
uv run docling あなたのファイル.pdf

ファイル名の代わりにURLを渡すこともできます。

docling https://arxiv.org/pdf/2408.09869

# uvの場合
uv run docling https://arxiv.org/pdf/2408.09869

Pythonから使う場合には次のコードが最小形です。変換結果を文字列で受け取り、Markdownファイルに保存します。

from pathlib import Path
from docling.document_converter import DocumentConverter

converter = DocumentConverter()
result = converter.convert("あなたのファイル.pdf")
md = result.document.export_to_markdown()
Path("あなたのファイル.md").write_text(md, encoding="utf-8")

具体的には、converter.convert(...)でPDFを読み込んで解析し、export_to_markdown()でその結果をMarkdown形式の文字列に変換し、最後のPath(...).write_text(...)でその文字列をファイルに書き出しています。

ファイル名の代わりに、URLも直接指定することができます。

result = converter.convert("https://arxiv.org/pdf/2408.09869")

Doclingの対応形式

本記事ではPDFファイルのみを扱いますが、PDFファイル以外にもWordファイル(.docx)、PowerPointファイル(.pptx)、Excelファイル(.xlsx)、HTMLファイル、画像ファイルなども、同じコードのまま変換できます。

ぺージを指定して変換する

数百ページのPDFをそのまま渡すと、数十分から1時間以上かかることがあります。実行中に途中経過は表示されないので、止まったように見えますが、動いています。メモリも数GB使うことがあります。

この場合、ページ範囲を指定して変換することもできます。指定は1始まりで、両端のページを含みます。

# 3〜4ページ目だけを変換する
result = converter.convert("あなたのファイル.pdf", page_range=(3, 4))

表をデータとして取り出す

Markdownを経由せず、表だけを取り出してCSVに変換できます。PDFの表をExcelに移したい場合の選択肢になります。

# resultはconvert()の戻り値
for i, table in enumerate(result.document.tables):
    df = table.export_to_dataframe(doc=result.document)
    df.to_csv(f"table_{i}.csv", index=False)

複数のファイルを処理する

DocumentConverter()は作るたびにAIモデルの読み込みが走るため、ループの中で毎回作ってはいけません。最初に1回だけ作り、全ファイルに使い回します。

次のコードはpdfsフォルダの中のpdfファイルを読み込み、outフォルダにMarkdownファイルを出力します。

from pathlib import Path
from docling.document_converter import DocumentConverter

converter = DocumentConverter()  # 最初に1回だけ作る
for pdf in Path("pdfs").glob("*.pdf"):
    out = Path("out", pdf.stem + ".md")
    if out.exists():
        continue  # 変換済みは飛ばす
    try:
        md = converter.convert(str(pdf)).document.export_to_markdown()
        out.write_text(md, encoding="utf-8")
    except Exception as e:
        print(f"FAILED: {pdf.name}: {e}") # 失敗した場合はファイル名だけ出力しておく

たくさんのPDFを処理すると、パスワード付きや破損など、必ず開けないファイルが混ざります。エラーになったファイルは飛ばして先へ進み、ファイル名だけ記録しておきます。あわせて、変換済みのファイルを飛ばすようにしておくと、途中で止まっても再実行するだけで続きから処理できます。

設定の変え方

PDFファイルに対して、OCRの無効化や数式の読み取りなど、Doclingの設定変更はoptsの部分を書き換えるだけです。

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions

opts = PdfPipelineOptions()
opts.do_ocr = False  # 例:文字の読み取り(OCR)を無効にする

converter = DocumentConverter(
    format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)}
)

opts.の後ろに指定できる項目は数十ありますが、よく使うものを以下に示します。

項目既定値効果備考
do_ocrTrueFalseでOCRを無効化
ocr_options.force_full_page_ocrFalseTrueで、テキスト層があっても無視してOCRし直す
do_formula_enrichmentFalseTrueで数式をLaTeX形式で抽出
generate_picture_imagesFalseTrueで画像をファイルとして保持。
image_modeで出力モードを変更する
「図や写真が消える」の節に詳細あり
images_scale1.0画像の解像度を変更します。1.0が標準品質、2.0が高解像度、0.5はプレビュー用の低解像度という位置「図や写真が消える」の節に詳細あり
table_structure_options.modeACCURATEFASTにすると表認識が速くなる
heading_hierarchy_options.enabledFalseTrueで見出しを章・節・項の階層に区別する(#/##/###)「見出しを階層構造にする」の節に詳細あり
artifacts_pathNoneAIモデルの読み込み先を指定

InputFormat.PDFというキーがあるとおり、この設定はPDFにだけ適用されます。

なお、本記事ではPythonから設定する方法を中心に説明していますが、ここで挙げた設定のいくつかはターミナルのコマンドからも指定できます。指定できる項目はdocling convert --helpで確認できます。

自分のPDFで試す

Doclingが自分の書類で使えるかは、変換して出力を見るのが確実です。試すファイルは、手持ちで一番厄介なもの(表が複雑、スキャン、段組みなど)を選ぶと、判断が早く済みます。

番号項目
1見出しが適切な階層で、章立ての順序どおりに並んでいるか
2がMarkdown形式の表になっているか。行と列の対応は合っているか
3スキャン書類でも文字が出ているか。
4ヘッダー・フッターが消えているか(消えるのが仕様)
5変換後のテキストが検索でヒットする
6数式が正しいか

なお、横倒しにスキャンされたページ、テキストのページとスキャンのページの混在、途中の空白ページ、2段組みのレイアウトは、特に設定しなくても自動で扱われます。

問題なければ、導入判断はこれで終わりです。以降は、出力がおかしいときへの対処方法について整理します。

症状別の対処

図や写真が消える

既定の設定では、ページ内の画像は出力に含まれず、あった場所に<!-- image -->という目印だけが残ります。グラフやチャートも画像として扱われるため、グラフが表している数値は取り出せません

図も一緒に残したい場合は、「設定の変え方」の形で画像の保持を有効にし、保存方法を切り替えます。

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling_core.types.doc import ImageRefMode

opts = PdfPipelineOptions()
opts.generate_picture_images = True  # 画像を保持する

converter = DocumentConverter(
    format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)}
)
result = converter.convert("あなたのファイル.pdf")
result.document.save_as_markdown("出力.md", image_mode=ImageRefMode.REFERENCED) # 保存方法を変更

最後の行のsave_as_markdownは、これまで使ってきたexport_to_markdown()(文字列を返すだけ)とは別の、ファイルへの保存まで行うメソッドです。image_modeは画像の埋め込み方を指定する引数で、REFERENCEDを指定すると画像を別ファイルとして書き出し、Markdownからはリンクで参照する形になります(他に、画像をMarkdownファイルの中にbase64形式でそのまま埋め込むEMBEDDED、画像を保存せず目印だけ残すPLACEHOLDERもあります)。

画像はPNGファイルとして別フォルダに書き出され、Markdownからリンクされます。

なお、画像が小さい場合には解像度を上げるか、埋め込み(EMBEDDED)にすると改善されます。解像度の変更は次のオプションを使用します。

opts.images_scale = 2.0  # 解像度を2倍

images_scaleは画像の解像度を上げるオプションで、画像の縦横の寸法そのものを指定するものではありません。寸法を揃えたい、あるいは特定の大きさにしたい場合は、取得した画像をリサイズします。

... (省略)
from pathlib import Path
from PIL import Image

opts = PdfPipelineOptions()
opts.generate_picture_images = True  # 画像を保持する
opts.images_scale = 6.0  # 解像度(大きめに取得した後にリサイズ)
... (省略)
result.document.save_as_markdown("出力.md", image_mode=ImageRefMode.REFERENCED) # 保存方法を変更

artifacts_dir = Path("出力_artifacts") # ここに画像を出力したフォルダを設定
TARGET_WIDTH = 480  # 揃える横幅
for img_path in artifacts_dir.glob("*.png"):
    img = Image.open(img_path)
    if img.width != TARGET_WIDTH:
        ratio = TARGET_WIDTH / img.width
        img = img.resize((TARGET_WIDTH, int(img.height * ratio)), Image.LANCZOS)
        img.save(img_path)  # 同じ場所に上書きする

画像を単にリサイズするとぼやけてしまうため、opts.images_scaleで解像度を大き目な値にしておきます。

VS codeで表示する場合

VS codeのプレビューでMarkdownを表示する場合、画像の相対パスは「./」から始めないと認識されないので注意が必要です。

数式が<!-- formula-not-decoded -->になる

数式の読み取りは初期設定では無効で、数式の場所にこの目印だけが残ります。「設定の変え方」の形で有効にすると、数式がLaTeXの記法で出力されます。

opts = PdfPipelineOptions()
opts.do_formula_enrichment = True

ただし品質は完全ではありません。分数や平方根といった式の形は取れても、綴りの誤りや、数式として表示するとエラーになる記号の混入が起きます。このため、Doclingで変換した後にチェックが必要です。

見出しを階層構造にする

Doclingはデフォルトでは見出しをすべて##にします。「1.」と「1.1」のように階層が異なる見出しを区別したい場合には、「設定の変え方」の形で次の設定を渡します。

opts = PdfPipelineOptions()
opts.heading_hierarchy_options.enabled = True

見出しの階層はPDFのしおり(目次情報)、章番号などの採番、フォントサイズの3種類の手がかりから推定されます。このうちフォントサイズによる推定はデフォルトでは効きません。動かすには、次の設定を同時に指定します。

opts.generate_parsed_pages = True  # フォントサイズによる見出し推定に必要

generate_parsed_pages=Trueを指定し忘れてもエラーにはなりません。フォントサイズによる推定だけが黙ってスキップされ、しおりや章番号による推定はそのまま働きます。

章番号のない見出しが多い書類(社内資料など)では、見出しの階層がうまく付かない場合に、この設定を使ってみてください。

本文にないはずの文章が混ざる

出どころは、ページに貼られた画像です。Doclingは写真や画面キャプチャの中の文字も読み取って本文に含めるため、たとえば資料に貼られたWebページのスクリーンショットがあると、その中の文字が地の文と区別なく出力に現れます。

スキャン書類を扱わないのであれば、文字の読み取り(OCR)ごと無効にすると、混入がなくなり処理も速くなります。「設定の変え方」の形で、次の設定を渡します。

opts = PdfPipelineOptions()
opts.do_ocr = False

変換後のテキストを検索してもヒットしない

目で見ると確かにその文字列が入っているのに、検索ではヒットしない場合があります。このときは、見た目がそっくりな別の文字が紛れています。PDFの内部では、画面上そっくりでもコンピュータ上は別物という文字が使われていることがあり、それがそのまま出力に出てくるためです。

「基本的な使い方」のexport_to_markdown()で受け取った文字列に対して、正規化処理を挟むと、こうした文字を普通の文字に揃えられます。

import unicodedata
...
md = result.document.export_to_markdown()
md = unicodedata.normalize("NFKC", md)  # ここで正規化する
...

ただし、この方法でも直りきらない文字がわずかに発生したり、「①」が「1」に変換されるなどの副作用もあるため、原文の表記を厳密に残したい書類(契約書の原本など)では、かけるかどうかの判断が必要になります。

NFKC正規化でも直らない場合は、テキスト層自体を無視してOCRで読み直す方法もあります。「設定の変え方」の形で、次の設定を渡します。

opts = PdfPipelineOptions()
opts.ocr_options.force_full_page_ocr = True

この方法はより精度の良い方法ですが、処理時間が長くなるデメリットがあります。このため、文書全体に使うのではなく、文字化けが疑わしいページなどに絞って試すのが現実的です。

日本語の単語の間にスペースが入る

「売 上」「九 州」のように、語の途中に半角スペースが入ることがあります。行またぎの文をつなぐ処理などで生じるもので、気になる場合だけ後処理で除去します。

import re
# ひらがな・カタカナ・漢字の間の半角スペースを除去
text = re.sub(r'([\u3040-\u30ff\u4e00-\u9fff]) (?=[\u3040-\u30ff\u4e00-\u9fff])', r'\1', text)

ただしこの方法は、「第Ⅰ部 特集」のような意図して入れられた区切りのスペースも一緒に消します。見出しの体裁が大事な文書では、かける範囲を選んでください。

スキャンの文字が欠ける

解像度の低いスキャンや、かすれ・低コントラストの原稿では、文の末尾などの文字が静かに消えたり、形の似た文字に誤読されたりすることがあります。欠けたことを機械的に検知する方法はありません。

スキャンし直せる場合は300dpi以上が目安になります。契約書など間違いが許されない書類は、変換結果を原本と突き合わせる前提で使います。

パスワード付きのPDFが変換できない

パスワードで保護されたPDFは、そのままでは開けずにエラーになります。パスワードが分かっている場合は、Doclingに渡せばそのまま変換できます。

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.backend_options import PdfBackendOptions

converter = DocumentConverter(
    format_options={
        InputFormat.PDF: PdfFormatOption(
            backend_options=PdfBackendOptions(password="パスワード")
        )
    }
)
result = converter.convert("保護あり.pdf")

ここで渡しているのは、「設定の変え方」で使ったpipeline_optionsではなくbackend_optionsです。pipeline_optionsはAIモデルの動かし方の設定、backend_optionsはPDFファイルの開き方の設定で、渡し口が分かれています。パスワードは後者にあたります。OCRの設定などと同時に指定したい場合は、PdfFormatOption()の中に両方を並べて書きます。

PdfFormatOption(
    pipeline_options=opts,                                   # OCRなど、処理の設定
    backend_options=PdfBackendOptions(password="パスワード"),  # PDFの開き方の設定
)

なお、パスワードをコードに直接書くと、そのままファイルやリポジトリに残ります。環境変数などから読み込むようにします

表が崩れる

セルを結合している表がある場合、記事執筆時点(2026年7月)では、Doclingは正しく表の構造を取得することができない場合があります。たとえば、以下の表をMarkdown化したとします。CPUの欄が結合セルになっています。

以下に、Markdown化した結果を示します。

このように表のフォーマットが崩れてしまいます。現状ではこれを改善するオプションはありません。このため、Markdown化した後に手動で表を修正するか、あるいは、表を画像に変換してリンクを埋め込むなどの方法があり得ます。

表を画像に変換する方法の欠点は、RAGで意味検索したときに引っかからないことですが、人が読む場合は問題ありません。

以下は表を画像に変換して埋め込むプログラムを試しに作ったものです。表の画像を取得するには、generate_page_images = Trueとした上でget_imageを使用します。

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions

from pathlib import Path
from PIL import Image

opts = PdfPipelineOptions()
opts.generate_page_images = True  # 表画像の取得に必要
opts.images_scale = 6.0  # 解像度(大きめに取得した後にリサイズ)

converter = DocumentConverter(
    format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)}
)
result = converter.convert("https://arxiv.org/pdf/2408.09869")
md = result.document.export_to_markdown()

img_dir = Path("output_articles")
img_dir.mkdir(exist_ok=True)
for i, table in enumerate(result.document.tables):
    table_md = table.export_to_markdown(doc=result.document)
    img = table.get_image(result.document) # 表を画像として取得する

    TARGET_WIDTH = 480  # 画像横幅
    if img.width != TARGET_WIDTH:
        ratio = TARGET_WIDTH / img.width
        # 画像をリサイズする
        img = img.resize((TARGET_WIDTH, int(img.height * ratio)), Image.LANCZOS)

    img_path = img_dir / f"table_{i}.png"
    img.save(img_path)
    md = md.replace(table_md, f"![Table {i}]({img_path})")

Path("output.md").write_text(md, encoding="utf-8")  # md変数を直接保存する

    Doclingで足りないとき

    変換品質が用途の要求に届かない場合は次のような代替品があります。

    ツール特徴注意点
    PyMuPDF4LLM高速ライセンス(AGPL)の制約があり、自社製品に組み込むには有償ライセンスの購入が必要になる場合がある
    MarkItDown(Microsoft製)高速・軽量。
    Word/Excel/HTML/音声など対応形式が多く、MITライセンス
    スキャン書類の読み取りは、外部のLLM API(GPT-4oなど)を呼び出す仕組み。
    UnstructuredWordやメールなどPDF以外の形式もまとめて扱いたいとき向き日本語のOCRには別途準備が必要
    LlamaParseクラウドサービス。複雑なレイアウトに強い有料サービス
    Mistral OCRクラウドサービス。数式・表の品質が高い有料サービス

    LlamaParseとMistral OCRはクラウドサービスで、文書の中身が外部のサーバーに送信されます。MarkItDownも、通常のWord/Excel変換はローカルで完結しますが、スキャン書類のOCRを使う場合は同様に外部へ送信されます。社外に出せない文書のOCRなら、ローカルで完結するDoclingが選択肢になります。

    まとめ

    本記事では、Doclingの基本的な使い方と設定方法などを解説しました。使えるかどうかは、まず自分のPDFで試してみるのが一番早いと思います。この際、大きなPDFを試すと時間がかかるため、page_rangeで数ページだけ試してみて感触をつかむと、無駄な待ち時間を避けられます。

    崩れやすいのは、表・スキャン画像・数式・日本語の文字コードです。正しく出力されているかを確認し、人による修正が必要な場合があります。

    社外に出せない文書は、ローカルで完結するDoclingかPyMuPDF4LLMを選びます。精度を優先できるならLlamaParseやMistral OCRのようなクラウドサービスも候補になります。

    参考資料

    -プログラミング
    -