LaravelでPDFを読み込むベストプラクティス
〜pdftotext・OCR・生成AIをどう組み合わせるべきか?〜
1. 先に結論:ベストプラクティスは「三段構え」
正確性を最優先にするなら、
LaravelでPDFを読み込むベストプラクティスは、ざっくり言うとこの三段構えになります。
-
PDFの種類を判定する
- 「テキスト埋め込みPDF」か
- 「スキャン画像PDF(テキストが選択できない)」か
- 「両方が混在しているハイブリッド」か
-
抽出レイヤーを分ける
- テキストPDF →
pdftotext系(例:spatie/pdf-to-text)でテキスト抽出 - 画像PDF → OCR(例:Tesseract+PHPラッパ/LaraOCR、もしくはクラウドOCR)
- テキストPDF →
-
構造化・要約・項目抽出に生成AIを使う
- 抽出したテキストを LLM(生成AI)で解釈・整形
- 「請求書の項目抽出」「履歴書の職歴抽出」「要約」などはここで行う
ポイントは、
「文字を読む役」= pdftotext / OCR
「意味を理解して整理する役」= 生成AI
と、役割をはっきり分けることです。
これを一つのレイヤーに混ぜると、
・遅い
・高コスト
・AIの“それっぽいけど間違った回答(ハルシネーション)”
が増えて、正確性が落ちます。
ここから、それぞれの手法の競合優位性と、
Laravelでの具体的な組み立て方を順番に見ていきますね。
2. そもそもPDF読み取りが難しい理由と「タイプ」の見分け方
2-1. PDFには大きく2種類ある
PDFは見た目は同じでも、中身の構造がまったく違います。
-
テキスト埋め込みPDF(デジタルPDF)
- WebシステムやWord/Excel、LaTeXなどから直接出力されたPDF
- PDF内部に「テキストオブジェクト」として文字情報が埋め込まれている
- 画面上で文字をドラッグ&コピーできる
-
画像PDF(スキャンPDF)
- 紙をスキャナーで取り込んだだけのPDF
- 中身は「画像」であって、テキストは一切埋め込まれていない
- 画面上で文字をドラッグしても選択できない
さらに最近は、
・表紙だけ画像
・中身はテキスト
のような ハイブリッドPDF も増えています。
2-2. まずは「テキストPDFかどうかの判定」を入れる
Laravel側では、例えばこんな流れが堅実です。
- PDFアップロード
spatie/pdf-to-textなどでテキスト抽出を試みる- 抽出結果の文字数や内容で判断
- ある程度文字数があれば「テキストPDF」
- ほとんど空なら「画像PDFとみなしてOCRへ」
use Spatie\PdfToText\Pdf;
$text = Pdf::getText(storage_path('app/'.$path));
if (mb_strlen(trim($text)) < 50) {
// 文字数が少ない → スキャンPDFの可能性大
// → OCRルートへ
} else {
// テキストPDFとして処理
}
spatie/pdf-to-text は内部で pdftotext コマンドを使っており、
Laravelでのテキスト抽出に関する解説でも定番になっています。
3. アプローチ①:pdftotext系(テキストPDF向け)
3-1. 仕組みとLaravelでの定番
pdftotext は Poppler というPDFライブラリに含まれるCLIツールで、
PDF内部のテキストオブジェクトを解析してプレーンテキストにしてくれます。
Laravelでは、ほぼ定番といっていいのがこのパッケージです。
spatie/pdf-to-textpdftotextをシンプルなPHPクラスでラップしたライブラリ- Laravelチュートリアルでも頻繁に採用されています
3-2. メリット(競合優位性)
1. テキストPDFなら精度はほぼ100%に近い
- PDF内部にある「テキストオブジェクト」をそのまま読み出すため、
誤認識による誤字が基本的にありません。 - OCRと違い、「画像を見て文字を推測する」わけではないので、
正確性の面では圧倒的に有利です。
2. 高速・低コスト
- 単純なテキスト抽出なので処理が軽く、
大量PDFのバッチ処理にも向いています。 - サーバーに
poppler-utilsを入れればライセンス的にも扱いやすく、
ランニングコストはほぼサーバー代だけです。
3. オンプレ完結・機微情報に強い
- 外部APIに投げる必要がないので、
個人情報や機密文書でも社内サーバーで完結できます。
3-3. デメリット・限界
- 画像PDFでは何も取れない(文字数ほぼ0)
- 表や段組み、レイアウト情報は壊れやすい
- テーブルは改行とスペースだけになる
- 2段組みの論文などは文章が混ざりやすい
つまり、
テキストPDFに対しては「速くて正確」だが、
レイアウトや構造までは面倒見ない
という立ち位置です。
4. アプローチ②:OCR(画像PDF・スキャン向け)
4-1. OCRとテキスト抽出の違い
-
テキスト抽出(pdftotextなど)
- PDF内部の「文字情報」を直接読む
- 文字が埋め込まれていないと何も取れない
-
OCR(Optical Character Recognition)
- 画像のピクセルを見て、そこに描かれた文字を推定する
- スキャンPDFや写真から文字を“復元”する技術
データ抽出系の解説でも、
OCRは「画像 → テキスト化」の役割
テキスト抽出は「すでにあるテキストから必要な情報を拾う」役割
と区別して説明されています。
4-2. Laravelでよく使われるOCRエンジン
1. Tesseract OCR
-
オープンソースで実績のあるOCRエンジン
-
多言語対応(日本語もサポート)
-
PHPからは以下のようなラッパーがよく使われています
thiagoalessio/tesseract_ocr(PHPラッパー)LaraOCR(Laravel向けラッパー)
2. Laravelの統合パッケージの例
NilGems/laravel-textract- 画像にはTesseract、PDFにはpdftotextを使い分ける統合パッケージ
- (最近のもの)
laravelsmartocr/laravel-smart-ocr- OCR+AIによるクレンジングやテンプレート機能をうたうパッケージ
4-3. OCRのメリット(競合優位性)
1. スキャンPDFや写真でも読める
- これはもはや「OCRでないとどうにもならない」世界です。
- 契約書の紙スキャン、領収書の写真、FAXのPDFなど、
現場ではまだまだこういったデータが多く存在します。
2. レイアウトをある程度維持できる
- 前処理や設定次第では、
段組みや表の列が分かりやすい形でテキストに落ちるケースもあります。 - とはいえ完璧ではないので、
表や帳票はあとでAIや専用パーサで補正する前提が安全です。
4-4. OCRのデメリット・注意点
1. 100%正しい文字にはならない
- 解像度・フォント・歪み・汚れなどで精度は変動します。
- 数字や記号などのミスはどうしても残るため、
金額やIDのような重要項目では人間の確認をはさむか、二重チェックが必要です。
2. 重い・遅い・コストがかかる
- 1ページを画像化→OCRという流れなので、
pdftotextと比べるとかなり重い処理です。 - 高解像度(300〜400dpi)でやるほど精度は上がりますが、
サーバー負荷や処理時間も伸びます。
3. 純粋なOCRだけでは「意味」は分からない
- OCRはあくまで「文字起こし」。
- どこが請求番号で、どこが日付かまでは理解しません。
この「意味を理解しない」という部分を補うのが、
次に出てくる生成AIや、クラウドのドキュメント解析サービス(Textract / Document AI / Form Recognizer など)です。
5. アプローチ③:生成AI(LLM)をどこに使うのが正解か
5-1. OCR vs AIの役割分担
最近の解説では、
OCRは「文字を読む」技術
AI(LLM)は「意味を理解して整理する」技術
と整理されることが増えています。
- OCR:画像から「A」「B」「3,000」などの文字列を起こす
- LLM:起こした文字から
- これは請求金額
- これは日付
- この請求書はどの会社からのものか
を判断してJSONなどに構造化
NVIDIA や各種ブログでも、
「PDF抽出はOCR+レイアウト解析+LLMの組み合わせが実務的」とされています。
5-2. 生成AIの強み
1. OCRのノイズに強い
多少の誤字があっても、
文脈から補完して正しく解釈してくれることが多いです。
2. 構造化が得意
- 「このPDFは請求書かどうか?」
- 「請求元企業名・振込先・合計金額・支払期限を抽出して」
といったタスクは、ルールベースよりもLLMの方が柔軟に対応できます。
3. 半構造化・非定型文書にも対応しやすい
- 職務経歴書
- 打ち合わせ議事録
- 長文の契約書
など、テンプレートがない文書でも、
「要約」「重要条項の抽出」などを行いやすいです。
5-3. 生成AIの弱み・気をつける点
1. ハルシネーション(それっぽい誤回答)
- 元PDFに書いていない内容を「補って」しまうことがあります。
- 正確性が重要な場面では、
「元テキストの範囲を超える仮説は禁止」といったプロンプト設計や、
抽出値と元テキストの突き合わせが必要です。
2. コストとレイテンシ
- 大きなPDFをそのままLLMに投げると、
トークン数が膨れ上がり、
コスト・時間ともにかなり重くなります。
3. 「抽出の一次ソース」にするのは危険
- いきなりPDF→LLMに投げて
「全文OCRもLLMでやらせる」方法は、
現状ではテキスト抜けや誤読が多く、
基盤となるテキスト抽出としてはまだリスクが高い とされます。
ですので、
文字を起こす:pdftotext / OCR
意味・構造を付ける:LLM
というレイヤー分離が、正確性とコストの両面からみて現実的です。
6. Laravelでのベストプラクティス構成(実装イメージ)
ここからは、実務でよくある要件を想定して、
Laravelアプリの構成イメージをお話しますね。
6-1. 全体アーキテクチャ
-
アップロード&メタ情報保存
pdfsテーブル:ファイルパス、状態、ページ数、種別(テキスト/画像/混在)など
-
抽出ジョブ(キュー)
DetectPdfTypeJob:spatie/pdf-to-textで試し読みして種別判定ExtractPdfTextJob:- テキストPDF → pdftotextで全ページ抽出
- 画像PDF → 1ページずつ画像化→OCR
-
AI構造化ジョブ
AnalyzePdfContentJob:- 抽出テキストをLLMに渡し、
請求書なら{ supplier, total_amount, due_date, invoice_number }
といったJSONを返させる
- 抽出テキストをLLMに渡し、
-
検証・管理画面
- オペレーターが抽出結果を確認・修正できるUI
- 特に金額・日付など重要項目は人間のチェックを前提にする
6-2. pdftotext(spatie/pdf-to-text)の利用例
# サーバーにpoppler-utilsをインストール(Ubuntu例)
apt install poppler-utils
# Laravelプロジェクトにパッケージ追加
composer require spatie/pdf-to-text
use Spatie\PdfToText\Pdf;
$pdfPath = storage_path('app/'.$pdf->path);
// 文字列抽出
$text = Pdf::getText($pdfPath);
// ページ別で処理したい場合は、別途ページ分割(pdftkなど)か
// pdftotextのオプションを活用
Laravel向けのチュートリアルでも、
ほぼ同じ構成で紹介されています。
6-3. OCRの利用例(Tesseract)
1) Tesseract+PHPラッパ
# Tesseract本体
apt install tesseract-ocr tesseract-ocr-jpn
composer require thiagoalessio/tesseract_ocr
use thiagoalessio\TesseractOCR\TesseractOCR;
$imgPath = storage_path('app/pages/page-1.png');
$text = (new TesseractOCR($imgPath))
->lang('jpn', 'eng')
->psm(3) // ページ分割モード
->run();
TesseractをLaravelから使う例は、
LaraOCR や各種ブログ・Q&Aでも一般的なパターンとして紹介されています。
2) PDF→画像変換
imagickやghostscriptを使って、PDFページを300dpi程度で画像化- その画像をTesseractに渡してテキストを抽出
という流れがベストプラクティスです。
6-4. 生成AIでの構造化(例:請求書)
抽出テキストを一度DBに保存したうえで、
LLMには「テキスト+抽出フォーマット」を渡します。
$prompt = <<<EOT
あなたは請求書のデータ抽出アシスタントです。
与えられたテキストから、次の項目をJSONで抽出してください。
- supplier_name: 請求元の会社名
- invoice_number: 請求書番号
- issue_date: 発行日(YYYY-MM-DD)
- due_date: 支払期限(YYYY-MM-DD)
- total_amount: 合計金額(数値のみ)
注意事項:
- 分からない項目は null にしてください。
- 元テキストに存在しない値を推測してはいけません。
- 応答はJSONのみとしてください。
=== テキスト ===
{$plainText}
EOT;
上のように 「推測禁止」「わからないときはnull」 を明記しておくと、
ハルシネーションをかなり減らせます。
7. ユースケース別:何を組み合わせるのがベストか?
7-1. 履歴書・職務経歴書の検索/自動タグ付け
- 多くはWordやPDF出力された「テキストPDF」
- レイアウトはバラバラ、内容もフリーフォーマット
おすすめ構成
- 抽出:
pdftotext(spatie/pdf-to-text) - 構造化:LLMで
- 職歴リスト
- スキル一覧
- 希望勤務地 などを抽出
- 検索:抽出した項目+全文をElasticsearch/Meilisearchなどに投入
7-2. 請求書・見積書・領収書の自動処理
- スキャンPDFが混ざる
- 金額や日付など「1文字のミスも困る」項目が多い
おすすめ構成
- 抽出:
- テキストPDF → pdftotext
- 画像PDF → Tesseract OCR or クラウドOCR(Textract / Document AI / Form Recognizer)
- 構造化:
- LLMでJSON化
- 重要項目(金額・日付・振込先など)は
- 人間のダブルチェック
- もしくは正規表現+ルールベースとの二重チェックで検証
精度がシビアであれば、
純粋なTesseractよりも クラウドのIDP(Document AI系) を使う選択肢も検討の余地があります。
これらはOCR+レイアウト解析+ML/LLMを組み合わせた
「ドキュメント処理専用AI」であり、
単純なOCRより精度と安定性が高いケースが多いと報告されています。
7-3. 契約書・規約・レポートの要約・ナレッジ化
- ほとんどがテキストPDF
- 「全文を構造化」よりも「要点をつかむ」ことが目的
おすすめ構成
- 抽出:pdftotext(テキストPDF)
- 構造化:LLMで
- 要約
- 条項の分類
- リスク項目のリストアップ
- 必要に応じて、全文テキストをベクターストアに入れてRAG検索
この場合は「文字の一字一句」よりも
要点の抜け漏れがないか が重要なので、
サンプリングして人間がレビューする仕組みを用意しておくと安心です。
8. 最後に:正確性を高めるための運用上のポイント
最後に、「正確性」を本気で求める場合に意識したいポイントをまとめます。
-
必ず「元PDF→抽出テキスト→構造化データ」をすべて保存する
- いつでも差分チェックができるように
-
OCR結果の品質チェックを仕組み化する
- 例えば
- 全角数字と半角数字が混ざっていないか
- 金額フィールドに「,」「.」以外の文字が入っていないか
- ルールベースのバリデーションで“怪しいもの”だけ人間が確認
- 例えば
-
LLMへの入力は「本当に必要な範囲」に絞る
- ページ単位・セクション単位で渡す
- 冗長なヘッダー・フッターは事前に除去
- これによりコストも精度も安定します
-
生成AIの役割はあくまで「整理・要約・抽出」と割り切る
- OCRの代替にしてしまうと、
見落としや誤読を検証するのが難しくなります。
- OCRの代替にしてしまうと、
9. まとめ
-
ベストプラクティスは三段構え
- PDF種別判定
- pdftotext / OCRでの一次抽出
- LLMでの意味理解・構造化
-
テキストPDFにはpdftotextが最適
- 高速・高精度・低コストで、Laravelでは
spatie/pdf-to-textが事実上の標準
- 高速・高精度・低コストで、Laravelでは
-
画像PDFにはOCRが必須
- Tesseract+Laravelラッパ(LaraOCR 等)
- 重要書類ではクラウドのIDPも検討価値あり
-
生成AIは「文字を読む」のではなく「意味を理解する」役割
- 項目抽出・分類・要約・エラー補正に使うと真価を発揮
-
正確性を上げる決め手は「レイヤー分離」と「検証フロー」
- 抽出レイヤーとAIレイヤーを分ける
- 重要項目は機械+人間の二重チェック
この考え方で設計しておくと、
LaravelアプリにPDF読み込み機能を追加しても、
後から「精度が足りない」「コストが高すぎる」といった行き詰まりを起こしにくくなります。
参考リンク(英語・日本語)
- spatie/pdf-to-text – PDFからテキスト抽出するPHPライブラリ(pdftotext利用)
- Laravelでpdftotextを使ってPDFからテキストを読み取る手順(日本語記事)
- Laravel 12でspatie/pdf-to-textを使ったPDFテキスト読み取りチュートリアル
- LaraOCR – Laravel向けTesseract OCRラッパ
- thiagoalessio/tesseract_ocr – PHP用Tesseractラッパ
- NilGems/laravel-textract – pdftotext+Tesseractを統合したLaravel用テキスト抽出パッケージ
- Tesseract OCRの基本とPHPからの利用解説(日本語記事)
- OCR vs テキスト抽出 vs IDP(Intelligent Document Processing)の違い解説
- ドキュメント抽出におけるOCRとLLMの比較と使い分け
