- 人工智能
- AI 技能/插件
- 提示工程
【免费下载链接】garden-skills
ConardLi's open-source Skills collection, featuring web design, knowledge retrieval, image generation, and more.
本文以 pdf_reading.md 为骨架,系统讲解在本地知识库检索场景(garden-skills 项目中的 kb-retriever Skill)下如何正确读取和分析 PDF:从 pdftotext 快速文本提取、pdfplumber 表格与布局提取,到扫描件 OCR、加密 PDF 解密与批量处理,再到结合 grep 的渐进式检索方法论。读完你将掌握一套"先学习、再处理、后检索"的 PDF 处理工作流,能够在真实知识库中高效提取信息而不浪费任何 token。
为什么先读本文档:kb-retriever 的强制学习机制
garden-skills 仓库中的 kb-retriever 是一个面向本地多格式知识库(Markdown / PDF / Excel)的检索与问答 Skill,其核心设计原则是先学习、再处理:当候选文件集合中出现 PDF 时,Agent 被强制要求先阅读 references/pdf_reading.md,理解推荐的工具与方法后,才能动手处理文件,严禁跳过学习步骤直接对原始二进制 PDF 进行检索。
在 SKILL.md 的"遇到 PDF 或 Excel 文件时的强制检查清单"中明确列出了四步:
- ✅ 已读取对应的 references 文档学习处理方法
- ✅ 已理解推荐的工具和命令
- ✅ 已将文件处理(提取/转换)完成
- ⏭️ 现在可以开始检索
并明确禁止:"在未读取 pdf_reading.md 的情况下直接尝试处理 PDF"、"跳过文件处理步骤,直接对原始 PDF/Excel 进行检索"。因此,本文档是任何 PDF 处理动作的前置知识,必须在使用之前完整阅读。
快速决策表:按场景选择工具
不同 PDF 的处理目标对应不同的工具选型。以下是核心决策依据:
| 场景 | 推荐工具 | 原因 | 命令/代码示例 |
|---|---|---|---|
| 纯文本提取(最常见) | pdftotext 命令 | 最快最简单 | pdftotext input.pdf output.txt |
| 需要保留布局 | pdftotext -layout | 保持原始排版 | pdftotext -layout input.pdf output.txt |
| 需要提取表格 | pdfplumber | 表格识别能力强 | page.extract_tables() |
| 需要元数据 | pypdf | 轻量级 | reader.metadata |
| 扫描PDF(图片) | OCR (pytesseract) | 无其他选择 | 先转图片再OCR |
从工具形态看,决策表实际上覆盖了三条路线:命令行工具(poppler-utils 全家桶)、Python 库(pypdf / pdfplumber / pypdfium2)以及OCR 兜底路线(pdf2image + pytesseract)。文档给出明确的推荐优先级(从高到低):
- pdftotext 命令行工具——最快,适合大多数 PDF
- pdfplumber——适合需要保留布局或提取表格
- pypdf——轻量级,适合简单提取
- OCR——仅用于扫描 PDF 或无法直接提取文本的情况
这一优先级与 kb-retriever 的整体性能理念一致:README.zh-CN.md 强调"渐进式检索,grep 优先 + 窗口读取,从不整文件加载,大语料下也能控制住 token"——而 pdftotext 作为最快的提取工具自然成为首选。
快速开始:使用 pdftotext(推荐路线)
pdftotext 来自 poppler-utils 工具包,是所有场景中最快的文本提取方案。使用时有一条铁律:必须将输出保存到文件,不要直接输出到终端(stdout),否则会占用大量 token。
# ✅ 正确:提取文本到文件(最快最简单) pdftotext input.pdf output.txt # ✅ 正确:保留布局并输出到文件 pdftotext -layout input.pdf output.txt # ✅ 正确:提取特定页面到文件 pdftotext -f 1 -l 5 input.pdf output.txt # 第1-5页 # ❌ 错误:不要使用 stdout(会占用大量 token) # pdftotext input.pdf -使用流程(这也是 kb-retriever 检索 PDF 的标准链路):
- 使用 pdftotext 提取文本到临时文件
- 使用 grep 或 Read 工具对生成的文本文件进行检索
- 只读取匹配部分的上下文,而非全文
在 SKILL.md 的 PDF 检索策略中,这一流程被进一步细化为完整工作流:读取处理方法指南 → 根据data_structure.md选择最相关的 1-3 个候选 PDF → 用 pdftotext 提取到文件 → 用 grep 对提取结果执行关键词搜索 → 对每个命中只读取命中附近范围的上下文(上下数十行或相邻几页)→ 保存「文件名 + 页码/大致位置 + 文本片段」→ 应用多轮迭代检索机制。
超大 PDF 的分页控制
对于超大 PDF,README.zh-CN.md 给出了明确的实践建议:使用按页范围抽取pdftotext -f 1 -l 10,对结果文本 grep,然后只读取匹配页面附近的内容,避免一次性处理整个文件。
Python 库详解
pypdf——轻量级基本文本提取与元数据
pypdf 是 BSD 许可的轻量级库,适合简单提取。它的两大优势是元数据读取与极低的依赖开销:
from pypdf import PdfReader reader = PdfReader("document.pdf") # 提取全部文本 for page in reader.pages: text = page.extract_text() print(text) # 提取元数据 meta = reader.metadata print(f"Title: {meta.title}") print(f"Author: {meta.author}") print(f"Subject: {meta.subject}") print(f"Creator: {meta.creator}")也可以先获取页数再决定是否继续:
from pypdf import PdfReader # 读取 PDF reader = PdfReader("document.pdf") print(f"Pages: {len(reader.pages)}") # 提取文本 text = "" for page in reader.pages: text += page.extract_text()pypdf 同时支持加密 PDF 的解密(详见下文"处理加密 PDF"章节),因此在需要处理受密码保护的文件时,它是首选。
pdfplumber——带布局的文本与表格提取
pdfplumber(MIT 许可)是结构化数据提取的主力,特别擅长表格识别与按坐标精确定位。它的 API 设计以page为操作单元,逐页处理天然适配"渐进式"理念。
提取文本(保留布局):
import pdfplumber with pdfplumber.open("document.pdf") as pdf: for page in pdf.pages: text = page.extract_text() print(text)提取表格:
with pdfplumber.open("document.pdf") as pdf: for i, page in enumerate(pdf.pages): tables = page.extract_tables() for j, table in enumerate(tables): print(f"Table {j+1} on page {i+1}:") for row in table: print(row)高级表格提取(转为 DataFrame):结合 pandas 可以将多页表格合并导出为 Excel,适合知识库中常见的数据报表类 PDF:
import pandas as pd with pdfplumber.open("document.pdf") as pdf: all_tables = [] for page in pdf.pages: tables = page.extract_tables() for table in tables: if table: # 检查表格非空 df = pd.DataFrame(table[1:], columns=table[0]) all_tables.append(df) # 合并所有表格 if all_tables: combined_df = pd.concat(all_tables, ignore_index=True) combined_df.to_excel("extracted_tables.xlsx", index=False)带坐标的精确文本提取:当只需要提取页面特定区域的文本(例如合同中的签字栏、表单中的指定字段)时,可以基于字符坐标或边界框操作:
import pdfplumber with pdfplumber.open("document.pdf") as pdf: page = pdf.pages[0] # 提取所有字符及其坐标 chars = page.chars for char in chars[:10]: # 前10个字符 print(f"Char: '{char['text']}' at x:{char['x0']:.1f} y:{char['y0']:.1f}") # 按边界框提取文本 (left, top, right, bottom) bbox_text = page.within_bbox((100, 100, 400, 200)).extract_text()复杂表格的高级设置:对于无明确边框线或线条断裂的复杂表格,可以自定义提取策略参数,并借助可视化调试定位问题:
import pdfplumber with pdfplumber.open("complex_table.pdf") as pdf: page = pdf.pages[0] # 自定义表格提取设置 table_settings = { "vertical_strategy": "lines", "horizontal_strategy": "lines", "snap_tolerance": 3, "intersection_tolerance": 15 } tables = page.extract_tables(table_settings) # 可视化调试 img = page.to_image(resolution=150) img.save("debug_layout.png")其中vertical_strategy/horizontal_strategy控制竖线与横线的检测策略(lines表示仅依赖实际绘制的线条),snap_tolerance控制线条吸附容差(像素),intersection_tolerance控制线条交点判定容差,取值越大越容易合并断裂的线条。渲染调试图可用于人工核对表格边界识别是否正确。
pypdfium2——快速渲染与文本提取
pypdfium2(Apache/BSD 许可)的强项是高性能页面渲染,即把 PDF 页面转成位图,这是 OCR 与可视化预览的关键步骤:
import pypdfium2 as pdfium # 加载 PDF pdf = pdfium.PdfDocument("document.pdf") # 提取文本 for i, page in enumerate(pdf): text = page.get_text() print(f"Page {i+1} text length: {len(text)} chars")将 PDF 页面渲染为图片:
import pypdfium2 as pdfium from PIL import Image pdf = pdfium.PdfDocument("document.pdf") # 渲染单页 page = pdf[0] # 第一页 bitmap = page.render( scale=2.0, # 高分辨率 rotation=0 # 不旋转 ) # 转换为 PIL Image img = bitmap.to_pil() img.save("page_1.png", "PNG") # 处理多页 for i, page in enumerate(pdf): bitmap = page.render(scale=1.5) img = bitmap.to_pil() img.save(f"page_{i+1}.jpg", "JPEG", quality=90)scale参数控制渲染倍率(2.0 即 200% 分辨率,适合后续 OCR 的精度要求),rotation控制旋转角度(0/90/180/270),quality是 JPEG 压缩质量(0-100,90 为高质量默认推荐值)。
命令行工具全家桶(poppler-utils)
除 pdftotext 外,poppler-utils 还提供渲染与图片提取工具,构成完整的命令行处理链路。
pdftotext——完整参数说明
# ✅ 提取文本到文件 pdftotext input.pdf output.txt # ✅ 保留布局提取到文件 pdftotext -layout input.pdf output.txt # ✅ 提取特定页面到文件 pdftotext -f 1 -l 5 input.pdf output.txt # 第1-5页 # ✅ 提取带坐标的文本到 XML 文件(用于结构化数据) pdftotext -bbox-layout document.pdf output.xml # ❌ 避免:不要省略输出文件名(会输出到 stdout) # pdftotext input.pdf参数含义:-layout尽可能保持原始排版(多栏文档建议开启);-f <页>/-l <页>指定提取页范围(从第 f 页到第 l 页);-bbox-layout输出包含每个词坐标信息的 XML,可用于后续结构化解析。
pdftoppm——高级图片转换
# 转换为 PNG,指定分辨率 pdftoppm -png -r 300 document.pdf output_prefix # 转换特定页面范围,高分辨率 pdftoppm -png -r 600 -f 1 -l 3 document.pdf high_res_pages # 转换为 JPEG,指定质量 pdftoppm -jpeg -jpegopt quality=85 -r 200 document.pdf jpeg_output-r <dpi>指定渲染分辨率(300 为印刷标准,600 适合高精度 OCR),-jpegopt quality=<0-100>控制 JPEG 压缩质量。
pdfimages——提取嵌入图片
当 PDF 本身包含图片资源(而非文本层)时,直接提取比渲染页面快得多:
# 提取所有图片 pdfimages -j input.pdf output_prefix # 列出图片信息(不提取) pdfimages -list document.pdf # 以原始格式提取 pdfimages -all document.pdf images/img-j输出 JPEG 格式,-list仅列出图片的页面位置、类型、尺寸等信息便于预筛选,-all以原始嵌入格式提取所有图片。
OCR 提取:扫描 PDF 的兜底方案
当 PDF 没有文本层(如扫描件、传真件),任何文本提取工具都会返回空结果。此时唯一的方案是 OCR:先把页面转成图片,再用 pytesseract 识别文字。
# 需要: pip install pytesseract pdf2image import pytesseract from pdf2image import convert_from_path # PDF 转图片 images = convert_from_path('scanned.pdf') # OCR 每一页 text = "" for i, image in enumerate(images): text += f"Page {i+1}:\n" text += pytesseract.image_to_string(image) text += "\n\n" print(text)仓库自带的兜底脚本
kb-retriever 在 scripts/convert_pdf_to_images.py 中提供了一个现成的 PDF 转图片工具,正是为"文本抽取一无所获的扫描版 PDF"准备的兜底脚本。它的实现逻辑值得参考:
import os import sys from pdf2image import convert_from_path def convert(pdf_path, output_dir, max_dim=1000): images = convert_from_path(pdf_path, dpi=200) for i, image in enumerate(images): # Scale image if needed to keep width/height under `max_dim` width, height = image.size if width > max_dim or height > max_dim: scale_factor = min(max_dim / width, max_dim / height) new_width = int(width * scale_factor) new_height = int(height * scale_factor) image = image.resize((new_width, new_height)) image_path = os.path.join(output_dir, f"page_{i+1}.png") image.save(image_path) print(f"Saved page {i+1} as {image_path} (size: {image.size})") print(f"Converted {len(images)} pages to PNG images")运行方式(命令行入口要求恰好两个参数):
python scripts/convert_pdf_to_images.py input.pdf output_dir/脚本要点:以 200 dpi 渲染每一页;当页面宽度或高度超过max_dim(默认 1000 像素)时按比例缩放,既控制图片体积又保留可读性;每页输出为page_{n}.png并打印实际尺寸。该脚本可作为 OCR 流程的前置步骤,把 PDF 页面批量转为 PNG 后再交给 pytesseract 识别,其自动缩放到 1000 像素以内的策略也天然适配 LLM 视觉输入的尺寸限制。
处理加密 PDF
知识库中常出现受密码保护的 PDF。pypdf 提供了解密 API,qpdf 命令行则适合脚本化批处理。
pypdf 解密:
from pypdf import PdfReader try: reader = PdfReader("encrypted.pdf") if reader.is_encrypted: reader.decrypt("password") # 解密后可正常提取文本 for page in reader.pages: text = page.extract_text() print(text) except Exception as e: print(f"Failed to decrypt: {e}")注意reader.is_encrypted用于先判断加密状态,decrypt("password")传入密码后即可正常提取。
qpdf 命令行解密:
# 使用 qpdf 解密(需要知道密码) qpdf --password=mypassword --decrypt encrypted.pdf decrypted.pdf # 检查加密状态 qpdf --show-encryption encrypted.pdf批量处理:整目录 PDF 文本提取
知识库通常是成百上千个 PDF 的集合,逐个处理不现实。文档给出了带日志与异常容错的批量提取脚本:
import os import glob from pypdf import PdfReader import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def batch_extract_text(input_dir): """批量提取文本""" pdf_files = glob.glob(os.path.join(input_dir, "*.pdf")) for pdf_file in pdf_files: try: reader = PdfReader(pdf_file) text = "" for page in reader.pages: text += page.extract_text() output_file = pdf_file.replace('.pdf', '.txt') with open(output_file, 'w', encoding='utf-8') as f: f.write(text) logger.info(f"Extracted text from: {pdf_file}") except Exception as e: logger.error(f"Failed to extract text from {pdf_file}: {e}") continue关键点:用glob收集目录内所有 PDF;逐文件 try/except 保证单个失败不影响整体;输出同名.txt文件;通过 logging 记录进度与错误。这与 kb-retriever "逐页或分块处理大文件"的内存管理原则一致。
性能优化:token 经济学的核心实践
处理 PDF 的最大风险不是 CPU 或内存,而是把大量文本直接灌进 LLM 上下文导致的 token 爆炸。文档给出的五条优化原则:
- 文件输出优先:始终将 pdftotext 输出保存到文件,然后用 grep/Read 检索,避免直接输出到终端占用大量 token
- 大型 PDF:使用流式方式逐页处理,避免一次性加载整个文件
- 文本提取:
pdftotext最快;pdfplumber 适合结构化数据和表格 - 图片提取:
pdfimages比渲染页面快得多 - 内存管理:逐页或分块处理大文件
其中第 1 条在 SKILL.md 中被反复强调:PDF 检索策略明确要求"使用pdftotext input.pdf output.txt将文本提取到文件,不要直接输出到 stdout(避免占用大量 token)"。整套 kb-retriever 的"grep 优先 + 窗口读取(limit ≈ 200–500 行)+ 最多 5 轮迭代收紧关键词"的渐进式检索机制,正是建立在这条优化原则之上。
快速参考表
| 任务 | 最佳工具 | 命令/代码 |
|---|---|---|
| 提取文本 | pdfplumber | page.extract_text() |
| 提取表格 | pdfplumber | page.extract_tables() |
| 命令行提取 | pdftotext | pdftotext -layout input.pdf |
| OCR 扫描PDF | pytesseract | 先转图片再OCR |
| 提取元数据 | pypdf | reader.metadata |
| PDF转图片 | pypdfium2 | page.render() |
可用包一览
| 包 | 用途 | 许可证 |
|---|---|---|
| pypdf | 基本操作 | BSD |
| pdfplumber | 文本和表格提取 | MIT |
| pypdfium2 | 快速渲染和提取 | Apache/BSD |
| pytesseract | OCR | Apache |
| pdf2image | PDF转图片 | MIT |
| poppler-utils | 命令行工具 | GPL-2 |
在 kb-retriever 中的完整落地流程
最后,将本文档与 SKILL.md 的总体流程串联起来,一个完整的 PDF 知识库检索闭环是:
- 导航:沿着每层目录的
data_structure.md判断答案可能在哪些文件(优先选择与问题主题高度匹配的领域目录和文件) - 学习:在处理 PDF 前必须先读取 references/pdf_reading.md(注意它位于 Skills 目录下而非 Knowledge 目录下),重点了解 pdftotext 命令、pdfplumber 用法、表格提取方法和快速决策表
- 处理:用推荐工具把 PDF 提取为文本文件(优先 pdftotext),表格用 pdfplumber,扫描件走 OCR
- 检索:grep 关键词定位,只读命中附近的窗口,保存「文件名 + 页码/大致位置 + 文本片段」
- 迭代:最多 5 轮,每轮收紧关键词(同义词、上下位词、业务缩写),直到信息足够支撑回答
回答时遵循"先给结论、再给依据、最后附来源"的格式(如"来源:design/api_gateway.pdf 第 3 页附近"),信息缺失时明确告知并提示用户缩小范围。这套从工具选型到 token 控制的方法论,就是 kb-retriever 在本地知识库场景下高效处理 PDF 的完整答案。
- 人工智能
- AI 技能/插件
- 提示工程
【免费下载链接】garden-skills
ConardLi's open-source Skills collection, featuring web design, knowledge retrieval, image generation, and more.
相关推荐
Garden Skills 之 kb-retriever:面向本地多格式知识库的分层索引与渐进式检索实战指南
Garden Skills 之 kb retriever:面向本地多格式知识库的分层索引与渐进式检索实战指南 导读 kb retriever 是 garden
人工智能AI 技能/插件提示工程Outlook PST 邮件取证分析完全指南:标准、工具链与实战工作流(Anthropic-Cybersecurity-Skills)
Outlook PST 邮件取证分析完全指南:标准、工具链与实战工作流(Anthropic Cybersecurity Skills) 导读 Microsoft
网络安全AI 技能/插件渗透测试红蓝对抗garden-skills 贡献与维护指南:Skill 新增、发版与 Release 工具链全解析
garden skills 贡献与维护指南:Skill 新增、发版与 Release 工具链全解析 本文档面向 garden skills 开源仓库的维护者与贡
人工智能AI 技能/插件提示工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考