0Pricing
AI Agents · レッスン

PyMuPDFとpdfplumberによるPDF解析

プログラムからPDFのテキスト、表、メタデータを抽出します。

「PyMuPDFとpdfplumberによるPDF解析」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。

PDFの解析が単純ではない理由

PDFはデータ形式ではなく、表示形式です。テキストは論理的な段落ではなく、位置が指定されたグリフとして保存されています。意味のあるテキストを抽出するには、レイアウト、読み取り順序、フォントの属性を理解し、複数段組み、ヘッダーやフッター、埋め込み画像などの例外的なケースにも対処する必要があります。

主に使われるライブラリは2つあります。PyMuPDF(速度)とpdfplumber(表の抽出)です。

PyMuPDFの基本

PyMuPDF(fitzとしてインポート)は、最も高速なPython用PDFライブラリです。テキストの抽出、メタデータ、画像、レンダリングに対応しています。pip install pymupdfでインストールします。

import fitz  # PyMuPDF

# Open a PDF
doc = fitz.open('document.pdf')

print(f'Pages: {len(doc)}')
print(f'Metadata: {doc.metadata}')

# Extract text from all pages
full_text = ''
for page_num in range(len(doc)):
    page = doc[page_num]
    text = page.get_text()  # plain text extraction
    full_text += f'--- Page {page_num + 1} ---\n{text}\n'

doc.close()
print(full_text[:500])

PyMuPDFのテキスト抽出モード

page.get_text()は、さまざまな出力形式に対応しています。プレーンテキストには'text'、バウンディングボックス付きのテキストブロックには'blocks'、フォント名やサイズを含む詳細な構造化出力には'dict'を使用します。

import fitz

doc = fitz.open('report.pdf')
page = doc[0]

# Mode 1: plain text
plain = page.get_text('text')

# Mode 2: blocks (each block has bbox + text)
blocks = page.get_text('blocks')
for block in blocks:
    x0, y0, x1, y1, text, block_no, block_type = block
    if block_type == 0:  # text block (1 = image)
        print(f'Block at ({x0:.0f},{y0:.0f}): {text[:80]}')

# Mode 3: dict (full detail including font info)
page_dict = page.get_text('dict')
for block in page_dict['blocks']:
    if block.get('type') == 0:  # text
        for line in block['lines']:
            for span in line['spans']:
                print(f"Font: {span['font']}, Size: {span['size']:.1f}, Text: {span['text']}")

doc.close()

ページメタデータの抽出

ページメタデータは、エージェントが文書構造を理解するのに役立ちます。ページ数、文書のタイトル、作成者、作成日、ページの寸法などが含まれます。PyMuPDFではこれらすべてを取得できます。

import fitz

def extract_pdf_metadata(filepath):
    doc = fitz.open(filepath)
    meta = doc.metadata
    info = {
        'title':    meta.get('title', 'Unknown'),
        'author':   meta.get('author', 'Unknown'),
        'subject':  meta.get('subject', ''),
        'creator':  meta.get('creator', ''),
        'created':  meta.get('creationDate', ''),
        'modified': meta.get('modDate', ''),
        'pages':    len(doc),
        'page_size': {
            'width':  doc[0].rect.width,
            'height': doc[0].rect.height
        }
    }
    doc.close()
    return info

meta = extract_pdf_metadata('contract.pdf')
print(f"Title: {meta['title']}, Pages: {meta['pages']}")

表の抽出に使うpdfplumber

pdfplumberは、PDFからの表の抽出に優れています。幾何学的な解析を使ってセルの境界を検出するため、明示的な表のマークアップがないPDFにも対応できます。

pip install pdfplumberでインストールします。

import pdfplumber

with pdfplumber.open('financial_report.pdf') as pdf:
    for page_num, page in enumerate(pdf.pages):
        tables = page.extract_tables()
        for table_idx, table in enumerate(tables):
            print(f'Page {page_num+1}, Table {table_idx+1}:')
            # table is a list of rows; each row is a list of cell strings
            headers = table[0]
            for row in table[1:]:
                row_dict = dict(zip(headers, row))
                print(row_dict)

pdfplumberの表設定

pdfplumberの表抽出は、さまざまな表のスタイルに対応できるよう、設定で調整できます。明示的な罫線、スペースで区切られた列、混在したレイアウトなどに対応します。

import pdfplumber

# Custom table settings for borderless tables
table_settings = {
    'vertical_strategy':   'text',   # 'lines', 'lines_strict', 'text', 'explicit'
    'horizontal_strategy': 'text',
    'snap_tolerance':       5,
    'join_tolerance':       3,
    'edge_min_length':     50,
    'min_words_vertical':   3,
    'min_words_horizontal': 1
}

with pdfplumber.open('nolines_table.pdf') as pdf:
    page = pdf.pages[0]
    table = page.extract_table(table_settings)
    if table:
        import csv
        import io
        output = io.StringIO()
        writer = csv.writer(output)
        writer.writerows(table)
        csv_string = output.getvalue()
        print(csv_string[:300])

複数段組みレイアウトへの対処

学術論文や新聞では、複数段組みのレイアウトが使われます。単純なテキスト抽出では段組みをまたいで左から右に読み取るため、壊れたテキストが生成されます。これを修正するには、まず列ごとにテキストブロックを並べ替えます。

import fitz

def extract_multicolumn_text(page, n_columns=2):
    page_width = page.rect.width
    col_width = page_width / n_columns

    blocks = page.get_text('blocks')
    # Filter text blocks only
    text_blocks = [b for b in blocks if b[6] == 0]

    # Assign each block to a column based on x position
    columns = [[] for _ in range(n_columns)]
    for block in text_blocks:
        x0 = block[0]
        col_idx = min(int(x0 / col_width), n_columns - 1)
        columns[col_idx].append(block)

    # Sort each column by vertical position
    for col in columns:
        col.sort(key=lambda b: b[1])  # sort by y0

    # Read columns left to right
    full_text = ''
    for col in columns:
        for block in col:
            full_text += block[4] + '\n'
    return full_text

ヘッダーとフッターのフィルタリング

PDFのヘッダーとフッターは各ページで繰り返され、抽出したテキストに不要な情報を混入させます。ページ上端または下端から10%以内という垂直位置を基準に特定し、コンテンツの抽出対象から除外します。

import fitz

def extract_without_headers_footers(page, margin_ratio=0.08):
    page_height = page.rect.height
    top_margin = page_height * margin_ratio
    bottom_margin = page_height * (1 - margin_ratio)

    blocks = page.get_text('blocks')
    content_blocks = []

    for block in blocks:
        x0, y0, x1, y1, text, block_no, block_type = block
        if block_type != 0:
            continue  # skip image blocks
        # Skip blocks in header or footer zone
        if y0 < top_margin or y1 > bottom_margin:
            continue
        content_blocks.append(text)

    return '\n'.join(content_blocks)

エージェント向けPDFテキストのチャンク化

長いPDFは、埋め込みと検索のためにチャンクへ分割する必要があります。段落、セクション、ページなどの自然な境界でチャンク化します。文の途中でコンテキストが途切れないよう、チャンク間にオーバーラップを設けます。

import fitz

def pdf_to_chunks(filepath, chunk_size=1000, overlap=200):
    doc = fitz.open(filepath)
    chunks = []

    for page_num in range(len(doc)):
        page = doc[page_num]
        page_text = page.get_text()

        # Split into chunks with overlap
        start = 0
        while start < len(page_text):
            end = start + chunk_size
            chunk = page_text[start:end]
            chunks.append({
                'text': chunk,
                'page': page_num + 1,
                'char_start': start,
                'source': filepath
            })
            start += chunk_size - overlap  # overlap

    doc.close()
    return chunks

chunks = pdf_to_chunks('research_paper.pdf')
print(f'Total chunks: {len(chunks)}')
print(f'Sample: {chunks[0]["text"][:200]}')

PyMuPDFとpdfplumberの組み合わせ

テキスト抽出にはPyMuPDF(高速)を、表の検出にはpdfplumber(より高精度)を使用します。結合抽出器は各ページで両方を実行し、テキストと表を分けた構造化コンテンツを返します。

import fitz
import pdfplumber

def full_pdf_extract(filepath):
    result = {'text_by_page': [], 'tables': []}

    # Text extraction with PyMuPDF
    doc = fitz.open(filepath)
    for i in range(len(doc)):
        text = extract_without_headers_footers(doc[i])
        result['text_by_page'].append({'page': i + 1, 'text': text})
    doc.close()

    # Table extraction with pdfplumber
    with pdfplumber.open(filepath) as pdf:
        for page_num, page in enumerate(pdf.pages):
            tables = page.extract_tables()
            for t in tables:
                result['tables'].append({
                    'page': page_num + 1,
                    'headers': t[0] if t else [],
                    'rows': t[1:] if t and len(t) > 1 else []
                })

    return result

抽出したテキストのファイルへの保存

PDFからテキストを抽出したら、後続のエージェントが利用できる形式で保存します。プレーンテキストファイルは埋め込みパイプラインに適しており、JSONは引用の追跡に必要なページ構造を保持します。

import json, os

class FakePage:
    def __init__(self, text): self.text = text
    def get_text(self): return self.text

class FakeDoc(list):
    def close(self): pass

def fitz_open(path):
    return FakeDoc([FakePage('Page 1 text'), FakePage('Page 2 text')])

def pdf_to_text_file(pdf_path, output_dir):
    doc = fitz_open(pdf_path)
    base = os.path.splitext(os.path.basename(pdf_path))[0]
    txt_path = os.path.join(output_dir, base + '.txt')
    with open(txt_path, 'w', encoding='utf-8') as f:
        for i, page in enumerate(doc):
            f.write(f'--- Page {i+1} ---\n{page.get_text()}\n')
    json_path = os.path.join(output_dir, base + '.json')
    pages = [{'page': i+1, 'text': p.get_text()} for i, p in enumerate(doc)]
    with open(json_path, 'w') as f:
        json.dump({'source': pdf_path, 'pages': pages}, f, indent=2)
    doc.close()
    print(f'Saved: {txt_path} and {json_path}')
    return txt_path, json_path

pdf_to_text_file('sample.pdf', '.')

理解度チェック

PDFファイルから表を抽出するのに最も適したPythonライブラリはどれですか。

復習:PyMuPDFとpdfplumberによるPDF解析

PyMuPDF(fitz)は、テキスト抽出、メタデータの取得、複数列レイアウトの処理に適した高速な選択肢です。pdfplumberは、設定可能な幾何学的戦略による表の抽出に優れています。

主なテクニックは、位置情報を考慮した抽出にget_text('blocks')を使用すること、垂直位置でヘッダーとフッターを除外すること、列ごとにブロックを並べ替えて複数列レイアウトに対応すること、そしてエージェントの検索システム向けにオーバーラップを持たせてテキストをチャンク化することです。最良の結果を得るには、両方のライブラリを組み合わせます。

よくある質問

「PyMuPDFとpdfplumberによるPDF解析」レッスンは無料ですか?

はい。「PyMuPDFとpdfplumberによるPDF解析」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。

「PyMuPDFとpdfplumberによるPDF解析」で何を学びますか?

プログラムからPDFのテキスト、表、メタデータを抽出します。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Agentsを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「PyMuPDFとpdfplumberによるPDF解析」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Agentsレッスンでコードを書いて実行できますか?

はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. PyMuPDFとpdfplumberによるPDF解析
  2. スキャン文書のOCR
  3. 複数文書Q&Aエージェント
  4. 文書の分類とルーティング
← AI Agentsに戻る