php elephant sticker
Photo by RealToughCandy.com on Pexels.com
目次

LaravelでPDFを読み込むベストプラクティス

〜pdftotext・OCR・生成AIをどう組み合わせるべきか?〜


1. 先に結論:ベストプラクティスは「三段構え」

正確性を最優先にするなら、
LaravelでPDFを読み込むベストプラクティスは、ざっくり言うとこの三段構えになります。

  1. PDFの種類を判定する

    • 「テキスト埋め込みPDF」か
    • 「スキャン画像PDF(テキストが選択できない)」か
    • 「両方が混在しているハイブリッド」か
  2. 抽出レイヤーを分ける

    • テキストPDF → pdftotext系(例:spatie/pdf-to-text)でテキスト抽出
    • 画像PDF → OCR(例:Tesseract+PHPラッパ/LaraOCR、もしくはクラウドOCR)
  3. 構造化・要約・項目抽出に生成AIを使う

    • 抽出したテキストを LLM(生成AI)で解釈・整形
    • 「請求書の項目抽出」「履歴書の職歴抽出」「要約」などはここで行う

ポイントは、

「文字を読む役」= pdftotext / OCR
「意味を理解して整理する役」= 生成AI

と、役割をはっきり分けることです。
これを一つのレイヤーに混ぜると、
・遅い
・高コスト
・AIの“それっぽいけど間違った回答(ハルシネーション)”
が増えて、正確性が落ちます。

ここから、それぞれの手法の競合優位性と、
Laravelでの具体的な組み立て方を順番に見ていきますね。


2. そもそもPDF読み取りが難しい理由と「タイプ」の見分け方

2-1. PDFには大きく2種類ある

PDFは見た目は同じでも、中身の構造がまったく違います。

  1. テキスト埋め込みPDF(デジタルPDF)

    • WebシステムやWord/Excel、LaTeXなどから直接出力されたPDF
    • PDF内部に「テキストオブジェクト」として文字情報が埋め込まれている
    • 画面上で文字をドラッグ&コピーできる
  2. 画像PDF(スキャンPDF)

    • 紙をスキャナーで取り込んだだけのPDF
    • 中身は「画像」であって、テキストは一切埋め込まれていない
    • 画面上で文字をドラッグしても選択できない

さらに最近は、
・表紙だけ画像
・中身はテキスト
のような ハイブリッドPDF も増えています。

2-2. まずは「テキストPDFかどうかの判定」を入れる

Laravel側では、例えばこんな流れが堅実です。

  1. PDFアップロード
  2. spatie/pdf-to-text などでテキスト抽出を試みる
  3. 抽出結果の文字数や内容で判断
    • ある程度文字数があれば「テキスト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-text
    • pdftotext をシンプルな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. 全体アーキテクチャ

  1. アップロード&メタ情報保存

    • pdfs テーブル:ファイルパス、状態、ページ数、種別(テキスト/画像/混在)など
  2. 抽出ジョブ(キュー)

    • DetectPdfTypeJobspatie/pdf-to-textで試し読みして種別判定
    • ExtractPdfTextJob
      • テキストPDF → pdftotextで全ページ抽出
      • 画像PDF → 1ページずつ画像化→OCR
  3. AI構造化ジョブ

    • AnalyzePdfContentJob
      • 抽出テキストをLLMに渡し、
        請求書なら { supplier, total_amount, due_date, invoice_number }
        といったJSONを返させる
  4. 検証・管理画面

    • オペレーターが抽出結果を確認・修正できる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→画像変換

  • imagickghostscript を使って、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. 最後に:正確性を高めるための運用上のポイント

最後に、「正確性」を本気で求める場合に意識したいポイントをまとめます。

  1. 必ず「元PDF→抽出テキスト→構造化データ」をすべて保存する

    • いつでも差分チェックができるように
  2. OCR結果の品質チェックを仕組み化する

    • 例えば
      • 全角数字と半角数字が混ざっていないか
      • 金額フィールドに「,」「.」以外の文字が入っていないか
    • ルールベースのバリデーションで“怪しいもの”だけ人間が確認
  3. LLMへの入力は「本当に必要な範囲」に絞る

    • ページ単位・セクション単位で渡す
    • 冗長なヘッダー・フッターは事前に除去
    • これによりコストも精度も安定します
  4. 生成AIの役割はあくまで「整理・要約・抽出」と割り切る

    • OCRの代替にしてしまうと、
      見落としや誤読を検証するのが難しくなります。

9. まとめ

  • ベストプラクティスは三段構え

    1. PDF種別判定
    2. pdftotext / OCRでの一次抽出
    3. LLMでの意味理解・構造化
  • テキストPDFにはpdftotextが最適

    • 高速・高精度・低コストで、Laravelでは spatie/pdf-to-text が事実上の標準
  • 画像PDFにはOCRが必須

    • Tesseract+Laravelラッパ(LaraOCR 等)
    • 重要書類ではクラウドのIDPも検討価値あり
  • 生成AIは「文字を読む」のではなく「意味を理解する」役割

    • 項目抽出・分類・要約・エラー補正に使うと真価を発揮
  • 正確性を上げる決め手は「レイヤー分離」と「検証フロー」

    • 抽出レイヤーとAIレイヤーを分ける
    • 重要項目は機械+人間の二重チェック

この考え方で設計しておくと、
LaravelアプリにPDF読み込み機能を追加しても、
後から「精度が足りない」「コストが高すぎる」といった行き詰まりを起こしにくくなります。


参考リンク(英語・日本語)

投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)