AI Agents · 课时

使用 PyMuPDF 和 pdfplumber 解析 PDF

以编程方式从 PDF 中提取文本、表格和元数据。

第 1 / 4 课13 个步骤

使用 PyMuPDF 和 pdfplumber 解析 PDF 是 CoddyKit 上的免费 AI Agents 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Agents 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Agents 课程共包含 4 节课。

为什么 PDF 解析并不简单

PDF 是一种展示格式,而不是数据格式。文本以带位置的字形存储,而不是逻辑段落。提取有意义的文本需要理解布局、阅读顺序和字体属性,并处理多栏布局、页眉/页脚以及嵌入图像等边界情况。

有两个库最为常用: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 与 PDF 表格解析器

使用 PyMuPDF 提取文本(速度更快),使用 PDF 表格解析器检测表格(准确度更高)。组合提取器会处理每一页,同时运行这两种提取方式,并返回结构化内容,将文本和表格分开。

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', '.')

知识检查

哪个 Python 库最适合从 PDF 文件中提取表格?

回顾:使用 PyMuPDF 和 PDF 表格解析器解析 PDF

PyMuPDF(fitz)是提取文本和元数据,以及处理多栏布局的快速选择。PDF 表格解析器擅长提取表格,并支持配置几何策略。

关键技术包括:使用 get_text('blocks') 进行与位置相关的提取;根据垂直位置过滤页眉和页脚;通过按列对文本块排序来处理多栏布局;以及为智能体检索系统创建带重叠的文本块。结合使用这两个库可以获得最佳效果。

免费开始

用 AI 导师学习 AI Agents — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
60
课程
239

常见问题解答

「使用 PyMuPDF 和 pdfplumber 解析 PDF」课时是免费的吗?

是的 — 「使用 PyMuPDF 和 pdfplumber 解析 PDF」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Agents 课程的其余内容,请升级到 CoddyKit PRO。 AI Agents 课程共包含 4 节课。

「使用 PyMuPDF 和 pdfplumber 解析 PDF」这节课中我会学到什么?

以编程方式从 PDF 中提取文本、表格和元数据。 你通过在浏览器中直接运行的动手代码来练习 AI Agents,全天候 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. 多文档问答代理
  4. 文档分类与路由
← 返回 AI Agents