使用 pdfplumber 解析 PDF 文件文字、表格、图片

1,033 阅读5分钟

项目简介

pdfplumber 项目(基于pdfminer.six开发),支持解析PDF文件,获取每个文本字符、矩形和线条的详细信息。此外还支持表格提取和可视化调试。

对于机器生成的PDF而言效果最佳,不适用于扫描得到的PDF。

支持:Python 3.8~3.11

加载PDF文件

处理PDF 请调用pdfplumber.open(x)方法,其中x可以是:

  • PDF 文件路径
  • 作为字节加载的文件对象
  • 作为字节加载的类似文件的对象
高级加载参数

要加载受密码保护的PDF,请传递password关键字参数,例如:pdfplumber.open("file.pdf", password="test")

pdfplumber.PDF 类(Top-level)

pdfplumber.PDF类表示一个独立的PDF文件,两个主要成员变量:

属性描述
.metadata一个由PDF的 Info 尾部信息中的元数据键/值对组成的字典。通常包括 "CreationDate," "ModDate," "Producer," 创建时间 修改时间 发行商等等。
.pages包含每个已加载页面的 pdfplumber.Page 实例的列表。

一个主要成员方法:处理大文件时需要考虑内存

方法描述
.close()默认情况下,Page 对象会缓存其布局和对象信息,以避免重新处理。然而,在解析大型PDF时,这些缓存的属性可能需要大量内存。你可以使用此方法来清除缓存并释放内存
import pdfplumber

pdf = pdfplumber.open("The_Old_Man_of_the_Sea.pdf")
print(pdf.metadata)

输出内容

{'CreationDate': "D:20060717205532+08'00'", 'Subject': 'For Personal Learning!', 'Author': 'Asiaing.com', 'Creator': 'PScript5.dll Version 5.2', 'Producer': 'Acrobat Distiller 7.0.5 (Windows)', 'ModDate': "D:20060717210222+08'00'", 'Title': 'Hemingway, Ernest - The Old Man and the Sea'}

pdfplumber.Page 类

import pdfplumber

pdf = pdfplumber.open("The_Old_Man_of_the_Sea.pdf")
pages = pdf.pages
print(pages[0].page_number)
print(pages[0].width, pages[0].height)

## 输出
1
405.0 591.0

pdfplumber.Page 类是 pdfplumber 的核心,表示PDF文件中一页单独的内容。

当我们使用 pdfplumber 时,大部分操作都会围绕这个类展开。

主要成员变量如下:

属性描述
.page_number顺序页码,从第一页开始为 1,第二页为 2,以此类推。
.width页面的宽度。
.height页面的高度。
.objects / .chars / .lines / .rects / .curves / .images这些属性都是列表,每个列表包含页面上嵌入的每个此类对象的一个字典。

提取单页文本

获取单页文本

import pdfplumber

pdf = pdfplumber.open("The_Old_Man_of_the_Sea.pdf")
pages = pdf.pages
p1_text = pages[1].extract_text()
print(p1_text)



# 获取单页文本(保留布局)
p1_text = pages[1].extract_text(layout=True)

print(p1_text)

提取单页表格

pdfplumber 对表格提取的方法大量借鉴了 Anssi Nurminen 的硕士论文,并受到 Tabula 的启发。

它的工作原理如下:

  1. 对于任何给定的PDF页面,找到那些(a)明确定义的线条和/或(b)由页面上单词的对齐方式暗示的线条。
  2. 合并重叠的,或几乎重叠的,线条。
  3. 找到所有这些线条的交点。
  4. 找到使用这些交点作为顶点的最精细的矩形集合(即,单元格)。
  5. 将连续的单元格分组成表格。

表格提取方法

pdfplumber.Page 对象可以调用以下方法:

方法描述
.find_tables(table_settings={})返回一个 Table 对象的列表。Table 对象提供对 .cells.rows,和 .bbox 属性的访问,以及 .extract(x_tolerance=3, y_tolerance=3) 方法。
.find_table(table_settings={})类似于 .find_tables(...),但返回页面上 最大 的表格,作为一个 Table 对象。如果多个表格的大小相同 —— 以单元格数量衡量 —— 此方法返回最接近页面顶部的表格。
.extract_tables(table_settings={})返回从页面上找到的 所有 表格中提取的文本,表示为一个列表的列表的列表,结构为 table -> row -> cell
.extract_table(table_settings={})返回从页面上 最大 的表格中提取的文本(参见上面的 .find_table(...)),表示为一个列表的列表,结构为 row -> cell
.debug_tablefinder(table_settings={})返回 TableFinder 类的一个实例,可以访问 .edges.intersections.cells,和 .tables 属性。

test.pdf 文件内容 image.png

代码示例

import pdfplumber

pdf = pdfplumber.open("test.pdf")
pages = pdf.pages

# 获取单页表格
p1_table = pages[0].extract_table()
print(p1_table)

# 输出内容
[['Fruit', 'Color', 'Price (USD)'], ['Apple', 'Red', '1.20'], ['Banana', 'Yellow', '0.50'], ['Orange', 'Orange', '0.80'], ['Strawberry', 'Red', '2.50'], ['Blueberry', 'Blue', '3.00'], ['Kiwi', 'Green', '1.00'], ['Mango', 'Orange', '1.50'], ['Grape', 'Purple', '2.00']]

提取页面图像

pdfplumber.Page 对象没有 extract_images 方法,所以不能直接从 PDF 页面中提取图像。

但是,可以通过页面操作来截取和获取图像,pdfplumber.Page类相关成员变量如下:

属性描述
.width页面的宽度。
.height页面的高度。
.objects / .chars / .lines / .rects / .curves / .images这些属性都是列表,每个列表包含页面上嵌入的每个此类对象的一个字典。

相关成员方法:

方法描述
.crop(bounding_box, relative=False, strict=True)返回裁剪到边界框的页面版本,边界框应表示为4元组,值为 (x0, top, x1, bottom)。裁剪的页面保留至少部分在边界框内的对象。如果对象只部分在框内,其尺寸将被切割以适应边界框。如果 relative=True,则边界框是从页面边界框的左上角偏移计算的,而不是绝对定位。(请参见 Issue #245 以获取视觉示例和解释。)当 strict=True(默认值)时,裁剪的边界框必须完全在页面的边界框内。
.within_bbox(bounding_box, relative=False, strict=True)类似于 .crop,但只保留 完全在 边界框内的对象。
.outside_bbox(bounding_box, relative=False, strict=True)类似于 .crop 和 .within_bbox,但只保留 完全在 边界框外的对象。
.filter(test_function)返回只有 test_function(obj) 返回 True 的 .objects 的页面版本。

原始pdf页面 image.png

裁剪后的图片 image.png

import pdfplumber

pdf = pdfplumber.open("test.pdf")
pages = pdf.pages
img = pages[1].images[0]
bbox = (img["x0"], img["top"], img["x1"], img["bottom"])

cropped_page = pages[1].crop(bbox)
# 可视化裁剪后的第二页
print(cropped_page.to_image())