Doclingは無料のオープンソースツールで、文書のレイアウトを解析して、表は表の形のまま、スキャン書類は文字を読み取って、Markdown形式のテキストにします。処理はすべて自分のパソコンの中で完結するので、社外に出せない書類にも使えます。
本記事ではDoclingの基本的な使い方を整理し、どのくらいまでPDFを正しく変換できるかを試しました。また、うまくいかなかったものに対して対処法があるものについては、その方法をまとめました。
Pythonの環境がまだの場合は「Python Install ManagerでのPython環境構築」や「uvのインストールと使い方」を先にご覧ください。
本文の記述は2026年7月時点のものです。
目次
インストール
pythonにdoclingのパッケージをインストールします。
pip install docling
# uvを使用する場合
uv add doclingDoclingは内部で使う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モデルのダウンロードが行われます。これには数分~数十分かかりますが、最初の一回だけになります。
基本的な使い方
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.09869Pythonから使う場合には次のコードが最小形です。変換結果を文字列で受け取り、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")ぺージを指定して変換する
数百ページの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_ocr | True | FalseでOCRを無効化 | |
ocr_options.force_full_page_ocr | False | Trueで、テキスト層があっても無視してOCRし直す | |
do_formula_enrichment | False | Trueで数式をLaTeX形式で抽出 | |
generate_picture_images | False | Trueで画像をファイルとして保持。image_modeで出力モードを変更する | 「図や写真が消える」の節に詳細あり |
images_scale | 1.0 | 画像の解像度を変更します。1.0が標準品質、2.0が高解像度、0.5はプレビュー用の低解像度という位置 | 「図や写真が消える」の節に詳細あり |
table_structure_options.mode | ACCURATE | FASTにすると表認識が速くなる | |
heading_hierarchy_options.enabled | False | Trueで見出しを章・節・項の階層に区別する(#/##/###) | 「見出しを階層構造にする」の節に詳細あり |
artifacts_path | None | AIモデルの読み込み先を指定 |
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で解像度を大き目な値にしておきます。
数式が<!-- 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"")
Path("output.md").write_text(md, encoding="utf-8") # md変数を直接保存するDoclingで足りないとき
変換品質が用途の要求に届かない場合は次のような代替品があります。
| ツール | 特徴 | 注意点 |
|---|---|---|
| PyMuPDF4LLM | 高速 | ライセンス(AGPL)の制約があり、自社製品に組み込むには有償ライセンスの購入が必要になる場合がある |
| MarkItDown(Microsoft製) | 高速・軽量。 Word/Excel/HTML/音声など対応形式が多く、MITライセンス | スキャン書類の読み取りは、外部のLLM API(GPT-4oなど)を呼び出す仕組み。 |
| Unstructured | WordやメールなどPDF以外の形式もまとめて扱いたいとき向き | 日本語のOCRには別途準備が必要 |
| LlamaParse | クラウドサービス。複雑なレイアウトに強い | 有料サービス |
| Mistral OCR | クラウドサービス。数式・表の品質が高い | 有料サービス |
LlamaParseとMistral OCRはクラウドサービスで、文書の中身が外部のサーバーに送信されます。MarkItDownも、通常のWord/Excel変換はローカルで完結しますが、スキャン書類のOCRを使う場合は同様に外部へ送信されます。社外に出せない文書のOCRなら、ローカルで完結するDoclingが選択肢になります。
まとめ
本記事では、Doclingの基本的な使い方と設定方法などを解説しました。使えるかどうかは、まず自分のPDFで試してみるのが一番早いと思います。この際、大きなPDFを試すと時間がかかるため、page_rangeで数ページだけ試してみて感触をつかむと、無駄な待ち時間を避けられます。
崩れやすいのは、表・スキャン画像・数式・日本語の文字コードです。正しく出力されているかを確認し、人による修正が必要な場合があります。
社外に出せない文書は、ローカルで完結するDoclingかPyMuPDF4LLMを選びます。精度を優先できるならLlamaParseやMistral OCRのようなクラウドサービスも候補になります。
参考資料
- Docling公式ドキュメント
https://docling-project.github.io/docling/ - Docling公式リポジトリ
https://github.com/docling-project/docling - PyMuPDF4LLM
https://pymupdf.readthedocs.io/en/latest/pymupdf4llm/ - MarkItDown
https://github.com/microsoft/markitdown - Unstructured
https://unstructured.io/ - LlamaParse
https://www.llamaindex.ai/pricing - Mistral OCR
https://mistral.ai/pricing/api/