一、关于 pdfplumber
- github : https://github.com/jsvine/pdfplumber
Plumb 一个 PDF 以获取每个文本字符、矩形和线条的详细信息。另外:表格提取和可视化调试。
在机器生成 而不是 扫描的PDF上 工作得最好。
在 pdfminer.six
基础上开发。
目前在 Python 3.8, 3.9, 3.10, 3.11 上 进行了测试。
本文件的翻译版本为:中文(by@hbh112233abc)。
要报告bug或请求功能,请提交问题。要提出问题或请求特定PDF的帮助,请使用讨论论坛。
安装
pip install pdfplumber
二、命令行界面
1、基本示例
curl "https://raw.githubusercontent.com/jsvine/pdfplumber/stable/examples/pdfs/background-checks.pdf" > background-checks.pdf
pdfplumber < background-checks.pdf > background-checks.csv
输出将是一个CSV,其中包含有关PDF中每个字符、行和矩形的信息。
2、选项
参数 | 描述 |
---|---|
--format [format] |
csv 或json 。json 格式返回更多信息;它包括PDF级别和页面级别的元数据,以及字典嵌套属性。 |
--pages [list of pages] |
以空格分隔, 1 索引页面列表或连字符页面范围。例如, 1, 11-15 ,它将返回第1、11、12、13、14和15页的数据。 |
--types [list of object types to extract] |
可选择 char 、rect 、line 、curve 、image 、annot 等等。默认为所有可用。 |
--laparams |
JSON格式的字符串( 例如,'{"detect_vertical": true}' )传递给pdfplumber.open(..., laparams=...) 。 |
--precision [integer] |
四舍五入浮点数的小数位数。默认为不四舍五入。 |
三、Python库
1、基本示例
import pdfplumber
with pdfplumber.open("path/to/file.pdf") as pdf:
first_page = pdf.pages[0]
print(first_page.chars[0])
2、加载PDF
要开始使用PDF,请调用pdfplumber.open(x)
,其中x
可以是:
- PDF文件的路径
- 文件对象,以字节形式加载
- 类似文件的对象,以字节形式加载
这个open
方法返回一个pdfplumber.PDF
类的实例。
要加载受密码保护的PDF,请传递password
关键字参数,例如pdfplumber.open("file.pdf", password = "test")
。
要将布局分析参数设置到pdfminer.six
的布局引擎,请传递laparams
关键字参数,例如pdfplumber.open("file.pdf", laparams = { "line_overlap": 0.7 })
。
要预规范化Unicode文本,传递unicode_norm=...
,其中...
是四种Unicode规范化形式之一:"NFC"
、"NFD"
、"NFKC"
或"NFKD"
。
默认情况下,无效的元数据值被视为警告。如果不是这样,请将strict_metadata=True
传递给open
方法,如果无法解析元数据,pdfplumber.open
将引发异常。
3、pdfplumber.PDF
类
顶级的pdfplumber.PDF
类表示单个PDF,并具有两个主要属性:
属性 | 描述 |
---|---|
.metadata |
元数据键/值对的字典,从PDF的Info 预告片中提取。通常包括“CreationDate”、“ModDate”、“Producer”等。 |
.pages |
每个加载的页面包含一个pdfplumber.Page 实例的列表。 |
… 并且还具有以下方法:
方法 | 描述 |
---|---|
.close() |
调用此方法会在每个页面上调用Page.close() ,并且还会关闭文件流(除了流是外部的情况,即已经打开并直接传递给pdfplumber )。 |
4、pdfplumber.Page
类
pdfplumber.Page
类是pdfplumber
的核心。
您将使用pdfplumber
做的大多数事情都将围绕这个类进行。
它具有以下主要属性:
属性 | 描述 |
---|---|
.page_number |
顺序页码,第一页从1 开始,第二页从2 开始,依此类推。 |
.width |
页面宽度。 |
.height |
页面高度。 |
.objects /.chars /.lines / .rects /.curves /.images |
这些属性中的每一个都是一个列表,每个列表都包含一个字典,用于嵌入在页面上的每个这样的对象。有关详细信息,请参阅下面的“对象”。 |
…以及这些主要方法:
方法 | 描述 |
---|---|
.crop(bounding_box, relative=False, strict=True) |
返回裁剪到边界框的页面版本,该版本应表示为带有值的4元组(x0, top, x1, bottom) 。裁剪的页面保留至少部分位于边界框内的对象。 如果对象仅部分位于框内,则对其尺寸进行切片以适应边界框。 如果 relative=True ,边界框的计算方法是与页面边界框左上角的偏移量,而不是绝对定位。(请参阅问题#245strict=True (默认值)时,裁剪的边界框必须完全在页面的边界框内。 |
.within_bbox(bounding_box, relative=False, strict=True) |
类似于.crop ,但只保留完全在边界框内的对象。 |
.outside_bbox(bounding_box, relative=False, strict=True) |
类似于.crop 和.within_bbox ,但只保留完全在边界框外的对象。 |
.filter(test_function) |
返回页面的一个版本,其中只有.objects``test_function(obj) 返回True 。 |
…并且还具有以下方法:
方法 | 描述 |
---|---|
.close() |
默认情况下,Page 对象缓存其布局和对象信息,以避免重新处理它。然而,在解析大型PDF时,这些缓存的属性可能需要大量内存。 您可以使用此方法刷新缓存并释放内存。 |
以下各节介绍了其他方法:
5、对象
每个pdfplumber.PDF
和pdfplumber.Page
实例 都提供对多种类型的PDF对象的访问,所有这些对象都派生自pdfminer.six
PDF解析。
以下属性分别返回匹配对象的Python列表:
.chars
,每个代表一个文本字符。.lines
,每个表示一条一维线。.rects
,每个表示一个二维矩形。.curves
,每个代表pdfminer.six
无法识别为线或矩形的任何一系列连接点。.images
,每个代表一个图像。.annots
,每个代表一个PDF注释(详细信息参见官方PDF规范的第8.4节).hyperlinks
,每个表示子类型Link
的单个PDF注释,并具有URI
操作属性
每个对象都表示为一个简单的Pythondict
,具有以下属性:
char
特性
属性 | 描述 |
---|---|
page_number |
找到此字符的页码。 |
text |
例如,z 或 Z 或 “”。 |
fontname |
字符字体的名称。 |
size |
字体大小。 |
adv |
等于文本宽度字体大小缩放系数。 |
upright |
字符是否直立。 |
height |
字符的高度。 |
width |
字符的宽度。 |
x0 |
字符左侧到页面左侧的距离。 |
x1 |
字符右侧到页面左侧的距离。 |
y0 |
字符底部到页面底部的距离。 |
y1 |
字符顶部到页面底部的距离。 |
top |
字符顶部到页面顶部的距离。 |
bottom |
字符底部到页面顶部的距离页面。 |
doctop |
字符顶部与文档顶部的距离。 |
matrix |
此字符的“当前转换矩阵”。(详见下文。) |
mcid |
此字符的标记内容部分ID(如果有)(否则None )。实验属性。 |
tag |
此字符的标记内容部分标签(如果有)(否则None )。实验属性。 |
ncs |
TKTK |
stroking_pattern |
TKTK |
non_stroking_pattern |
TKTK |
stroking_color |
字符轮廓(即笔画)的颜色。详见docs/colors.md。 |
non_stroking_color |
字符的内部颜色。详见docs/colors.md。 |
object_type |
“char” |
注意:字符的matrix
属性表示“当前转换矩阵”,如PDF参考(第6版)第4.2.2节所述。
矩阵控制字符的缩放、倾斜和位置平移。旋转是缩放和倾斜的组合,但在大多数情况下可以认为等于x轴倾斜。
pdfplumber.ctm
子模块定义了一个类CTM
,它有助于这些计算。例如:
from pdfplumber.ctm import CTM
my_char = pdf.pages[0].chars[3]
my_char_ctm = CTM(*my_char["matrix"])
my_char_rotation = my_char_ctm.skew_x
line
属性
属性 | 描述 |
---|---|
page_number |
找到此行的页码。 |
height |
行的高度。 |
width |
行的宽度。 |
x0 |
左端距页面左侧的距离。 |
x1 |
右端距页面左侧的距离。 |
y0 |
下端距页面底部的距离。 |
y1 |
上端距页面底部的距离。 |
top |
行顶距页面顶部的距离。 |
bottom |
行底距页面顶部的距离。 |
doctop |
行顶距文档顶部的距离。 |
linewidth |
线条的厚度。 |
stroking_color |
线条的颜色。详见docs/colors.md。 |
non_stroking_color |
为线条路径指定的非描边颜色。详见docs/colors.md。 |
mcid |
此行的标记内容部分ID(如果有)(否则None )。实验属性。 |
tag |
此行的标记内容部分标记(如果有)(否则为None )。实验属性。 |
object_type |
“line” |
rect
属性
属性 | 描述 |
---|---|
page_number |
找到此矩形的页码。 |
height |
矩形的高度。 |
width |
矩形的宽度。 |
x0 |
矩形左侧到页面左侧的距离。 |
x1 |
矩形右侧到页面左侧的距离。 |
y0 |
矩形底部到页面底部的距离。 |
y1 |
矩形顶部到页面底部的距离。 |
top |
矩形顶部到页面顶部的距离。 |
bottom |
矩形底部到页面顶部的距离。 |
doctop |
矩形顶部到文档顶部的距离。 |
linewidth |
线粗细。 |
stroking_color |
矩形轮廓的颜色。详见docs/colors.md。 |
non_stroking_color |
矩形的填充颜色。详见docs/colors.md. |
mcid |
此rect的标记内容部分ID(如果有)(否则None )。实验属性. |
tag |
此rect的标记内容部分标记(如果有)(否则None )。实验属性。 |
object_type |
“rect” |
curve
属性
属性 | 描述 |
---|---|
page_number |
找到此曲线的页码。 |
pts |
一个(x, top) 元组列表,表示曲线上的点。 |
path |
一个(cmd, *(x, top)) 元组列表描述完整的路径描述,包括(例如)贝塞尔曲线中使用的控制点。 |
height |
曲线边界框的高度。 |
width |
曲线边界框的宽度。 |
x0 |
曲线最左边点到页面左侧的距离。 |
x1 |
曲线最右边点到页面左侧的距离。 |
y0 |
曲线最低点到页面底部的距离。 |
y1 |
曲线最高点到页面底部的距离。 |
top |
曲线最高点到页面顶部的距离。 |
bottom |
距离 |
doctop |
曲线最高点与文档顶部的距离。 |
linewidth |
线的厚度。 |
fill |
曲线路径定义的形状是否被填充。 |
stroking_color |
曲线轮廓的颜色。详见docs/colors.md。 |
non_stroking_color |
曲线的填充颜色。详见docs/colors.md。 |
dash |
A([dash_array], dash_phase) 描述曲线破折号样式的元组。详见PDF规范的表4.6。 |
mcid |
此曲线的标记内容节ID(否则None )。实验属性。 |
tag |
此曲线的标记内容节标签(否则None <–atag–20/>)。实验属性。 |
object_type |
“曲线” |
派生属性
此外,pdfplumber.PDF
和pdfplumber.Page
都提供了 对多个派生对象列表的访问:.rect_edges
(将每个矩形分解为四行),.curve_edges
(对curve
对象做同样的事情)和.edges
(结合了.rect_edges
、.curve_edges
和.lines
)。
image
属性
注意:虽然image
对象的定位和特征可以通过pdfplumber
获得,但此库不提供对重建图像内容的直接支持。为此,请参阅此建议。
属性 | 描述 |
---|---|
page_number |
找到图像的页码。 |
height |
图像的高度。 |
width |
图像的宽度。 |
x0 |
图像左侧到页面左侧的距离。 |
x1 |
图像右侧到页面左侧的距离。 |
y0 |
图像底部到页面底部的距离。 |
y1 |
图像顶部到页面底部的距离。 |
top |
图像顶部到页面顶部的距离。 |
bottom |
图像底部到页面顶部的距离。 |
doctop |
矩形顶部到文档顶部的距离。 |
srcsize |
图像原始尺寸,作为(width, height) 元组。 |
colorspace |
图像的颜色域(例如,RGB)。 |
bits |
|
stream |
图像的像素值,作为pdfminer.pdftypes.PDFStream 对象。 |
imagemask |
一个可为空的布尔值;如果True ,“指定图像数据将用作以当前颜色绘制的模板蒙版。” |
mcid |
此图像的标记内容部分ID(如果有)(否则None )。实验属性。 |
tag |
此图像的标记内容部分标签(如果有)(否则None )。实验属性。 |
object_type |
“图像” |
6、通过pdfminer获取更高级别的pdfminer.six
如果您将pdfminer.six
-处理laparams
参数传递给pdfplumber.open(...)
,那么每个页面的.objects
字典也将包含pdfminer.six
的高级布局对象,例如"textboxhorizontal"
。
四、可视化调试
pdfplumber
的可视化调试工具有助于理解PDF的结构和从中提取的对象。
1、创建一个PageImage
与.to_image()
要将任何页面(包括裁剪的页面)转换为PageImage
对象,请调用my_page.to_image()
。您可以选择传递以下关键字参数之一:
resolution
:每英寸所需的像素数。默认值:72
。类型:int
。width
:所需的图像宽度(以像素为单位)。默认值:未设置,由resolution
决定。类型:int
。height
:所需的图像宽度,以像素为单位。默认值:未设置,由resolution
决定。类型:int
。antialias
:创建图像时是否使用抗锯齿。设置为True
会创建具有较少锯齿状文本和图形但具有较大文件大小的图像。默认值:False
。类型:bool
。force_mediabox
:使用页面的.mediabox
维度,而不是.cropbox
维度。默认值:False
。类型:bool
。
例如:
im = my_pdf.pages[0].to_image(resolution=150)
在脚本或REPL中,im.show()
将在本地图像查看器中打开图像。但是PageImage
对象也可以很好地与Jupyter笔记本配合使用;它们会自动呈现为单元格输出。例如:
注意:.to_image(...)
按预期工作与Page.crop(...)
/CroppedPage
实例,但无法合并通过Page.filter(...)
/FilteredPage
实例所做的更改。
2、基本PageImage
方法
方法 | 描述 |
---|---|
im.reset() |
清除到目前为止绘制的任何内容。 |
im.copy() |
将图像复制到一个新的PageImage 对象。 |
im.show() |
在本地图像查看器中打开图像。 |
im.save(path_or_fileobject, format="PNG", quantize=True, colors=256, bits=8) |
将带注释的图像保存为PNG文件。默认参数将图像量化为256种颜色的调色板,保存具有8位颜色深度的PNG。您可以通过传递quantize=False 或通过传递colors=N 来调整调色板的大小。 |
3、绘图方法
您可以将显式坐标或任何pdfplumber
PDF对象(例如char、line、rect)传递给这些方法。
单对象方法 | 批量方法 | 描述 |
---|---|---|
im.draw_line(line, stroke={color}, stroke_width=1) |
im.draw_lines(list_of_lines, **kwargs) |
从line 、curve 或2元组的2元组(例如,((x, y), (x, y)) )中绘制一条线。 |
im.draw_vline(location, stroke={color}, stroke_width=1) |
im.draw_vlines(list_of_locations, **kwargs) |
在location . |
im.draw_hline(location, stroke={color}, stroke_width=1) |
im.draw_hlines(list_of_locations, **kwargs) |
在location 指示的y坐标处绘制一条水平线。 |
im.draw_rect(bbox_or_obj, fill={color}, stroke={color}, stroke_width=1) |
im.draw_rects(list_of_rects, **kwargs) |
从rect``char |
im.draw_circle(center_or_obj, radius=5, fill={color}, stroke={color}) |
im.draw_circles(list_of_circles, **kwargs) |
在(x, y) 坐标或char 、rect 等的中心绘制一个圆。 |
注意:上面的方法建立在Pillow的ImageDraw
方法上,但是参数已经过调整,以与SVG的fill
/stroke
/stroke_width
术语保持一致。
4、可视化调试表查找器
im.debug_tablefinder(table_settings={})
将返回PageImage的一个版本,其中覆盖了检测到的线(红色)、交叉点(圆圈)和表格(浅蓝色)。
五、提取文本
pdfplumber
可以从任何给定页面(包括裁剪和派生页面)中提取文本。它还可以尝试保留该文本的布局,以及识别单词和搜索查询的坐标。Page
对象可以调用以下文本提取方法:
1、.extract_text(x_tolerance=3, x_tolerance_ratio=None, y_tolerance=3, layout=False, x_density=7.25, y_density=13, line_dir_render=None, char_dir_render=None, **kwargs)
将页面的所有字符对象整理成一个字符串。
- 当
layout=False
时:添加一个字符的x1
和下一个字符的x0
之间的差异大于x_tolerance
。
(如果x_tolerance_ratio
不是None
,则提取器使用动态x_tolerance
等于x_tolerance_ratio * previous_character["size"]
。)
添加换行符,其中一个字符的doctop
和下一个字符的doctop
之间的差异大于y_tolerance
- 当
layout=True
(实验特征):尝试模仿页面上文本的结构布局,使用x_density
和y_density
确定每个“点”的最小字符/换行符数,PDF测量单位。
传递line_dir_render="ttb"/"btt"/"ltr"/"rtl"
和/或char_dir_render="ttb"/"btt"/"ltr"/"rtl"
将以与默认不同的方向输出行/字符。
所有剩余的**kwargs
被传递到.extract_words(...)
(见下文),计算布局的第一步。
2、.extract_text_simple(x_tolerance=3, y_tolerance=3)
稍快但不太灵活的版本.extract_text(...)
,使用更简单的逻辑。
3、.extract_words(x_tolerance=3, x_tolerance_ratio=None, y_tolerance=3, keep_blank_chars=False, use_text_flow=False, line_dir="ttb", char_dir="ltr", line_dir_rotated="ttb", char_dir_rotated="ltr", extra_attrs=[], split_at_punctuation=False, expand_ligatures=True, return_chars=False)
返回所有看起来像单词的东西及其边界框的列表。
单词被认为是字符序列,其中(对于“直立”字符)的x1
和下一个字符的x0
之间的差异小于或等于x_tolerance
和其中一个字符的doctop
和下一个字符的doctop
小于或等于y_tolerance
.(如果x_tolerance_ratio
不是None
,提取器使用动态x_tolerance
等于x_tolerance_ratio * previous_character["size"]
.)非直立字符也采用了类似的方法,但测量它们之间的垂直距离,而不是水平距离。
将keep_blank_chars
更改为True
将意味着空白字符被视为单词的一部分,而不是单词之间的空格。
将use_text_flow
更改为True
将使用PDF的基础字符流作为单词排序和分割的指南,而不是按x/y位置预先排序字符。
(这模仿了拖动光标如何突出显示PDF中的文本;因此,顺序似乎并不总是合乎逻辑的。)line_dir
和char_dir
参数告诉此方法预期读取行/字符的方向;有效选项是“ttb”(从上到下)、“btt”(从下到上)、“ltr”(从左到右)和“rtl”(从右到左)。
line_dir_rotated
和char_dir_rotated
参数相似,但用于已旋转的文本。
传递extra_attrs
(例如,["fontname", "size"]
将每个单词限制为与每个属性共享完全相同值的字符,生成的单词字典将指示这些属性。
将split_at_punctuation
设置为True
将在以下指定的标点符号处强制执行中断标记string.punctuation
;或者您可以通过传递字符串来指定分隔标点符号的列表,例如,split_at_punctuation='!"&'()*+,.:;<=>?@[]^
{|}~'。 除非您设置
expand_ligatures=False,否则连字符如
fi将扩展为它们的组成字母(例如,
fi)。 传递
return_chars=True将向每个单词字典添加其组成字符的列表,作为
"chars"`字段中的列表。
4、.extract_text_lines(layout=False, strip=True, return_chars=True, **kwargs)
实验功能,返回表示页面上文本行的字典列表。
strip
参数的工作方式类似于Python的str.strip()
方法,并返回没有周围空格的text
属性。
(仅在layout = True
时相关。)将return_chars
设置为False
将从返回的文本行字典中排除单个字符对象。
其余的**kwargs
是您将传递给.extract_text(layout=True, ...)
。
5、.search(pattern, regex=True, case=True, main_group=0, return_groups=True, return_chars=True, layout=False, **kwargs)
实验功能,允许您搜索页面的文本,返回与查询匹配的所有实例的列表。
对于每个实例,响应字典对象包含匹配的文本、任何正则表达式组匹配、边界框坐标和char对象本身。
pattern
可以是编译的正则表达式、未编译的正则表达式或非正则表达式字符串。
如果regex
是False
,则该模式被视为非正则表达式字符串。
如果case
是False
,则以不区分大小写的方式执行搜索。
设置main_group
将结果限制为pattern
内的特定正则表达式组(默认值为0
表示整个匹配)。
将return_groups
和/或return_chars
设置为False
将排除添加匹配的正则表达式组和/或字符的列表(作为"groups"
和"chars"
到返回字典)。
layout
参数的操作方式与.extract_text(...)
一样。
其余的**kwargs
是您将传递给的.extract_text(layout=True, ...)
。
注意:零宽度和全空白匹配被丢弃,因为它们(通常)在页面上没有明确的位置。
6、.dedupe_chars(tolerance=1, extra_attrs=("fontname", "size"))
返回具有重复字符的页面版本-那些共享相同文本、定位(在tolerance
x/y内)和与其他字符相同的字符-已删除。
(请参阅extra_attrs
问题#71以了解动机。) |
六、提取表
pdfplumber
的表格检测方法大量借鉴了Anssi Urminen的硕士论文,并受到Tabula的启发。它的工作原理是这样的:
- 对于任何给定的PDF页面,找到(a)明确定义和/或(b)由页面上的单词对齐暗示的行。
- 合并重叠或几乎重叠的线。
- 找出所有这些线的交点。
- 找到使用这些交点作为顶点的最粒度的矩形集(即单元格)。
- 将连续的单元格分组到表格中。
1、表提取方法
pdfplumber.Page
对象可以调用以下表方法:
方法 | 描述 |
---|---|
.find_tables(table_settings={}) |
返回Table 对象的列表。Table 对象提供对.cells 、.rows 、.columns 和.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 属性。 |
例如:
pdf = pdfplumber.open("path/to/my.pdf")
page = pdf.pages[0]
page.extract_table()
2、表提取设置
默认情况下,extract_tables
使用页面的垂直线和水平线(或矩形边缘)作为单元格分隔符。但是该方法可以通过table_settings
参数进行高度自定义。可能的设置及其默认值:
{
"vertical_strategy": "lines",
"horizontal_strategy": "lines",
"explicit_vertical_lines": [],
"explicit_horizontal_lines": [],
"snap_tolerance": 3,
"snap_x_tolerance": 3,
"snap_y_tolerance": 3,
"join_tolerance": 3,
"join_x_tolerance": 3,
"join_y_tolerance": 3,
"edge_min_length": 3,
"min_words_vertical": 3,
"min_words_horizontal": 1,
"intersection_tolerance": 3,
"intersection_x_tolerance": 3,
"intersection_y_tolerance": 3,
"text_tolerance": 3,
"text_x_tolerance": 3,
"text_y_tolerance": 3,
"text_*": …, # See below
}
设置 | 描述 |
---|---|
"vertical_strategy" |
要么是"lines" 、"lines_strict" 、"text" 、或"explicit" 。见下文解释。 |
"horizontal_strategy" |
要么是"lines" 、"lines_strict" 、"text" 、或"explicit" 见下文解释。 |
"explicit_vertical_lines" |
明确划分表格中单元格的垂直线列表。可以与上述任何策略结合使用。列表中的项目应该是数字——指示线条的x 坐标(页面的全部高度)——或者line /rect /curve 对象。 |
"explicit_horizontal_lines" |
明确划分表格中单元格的水平线列表。可以与上述任何策略结合使用。列表中的项目应该是数字——表示一条线的y 坐标(页面的全部高度)——或者line /rect /curve 对象。 |
"snap_tolerance" 、"snap_x_tolerance" 、"snap_y_tolerance" |
snap_tolerance 点内的平行线将被“折断”到相同的水平或垂直位置。 |
"join_tolerance" 、"join_x_tolerance" 、"join_y_tolerance" |
同一无穷线上的线段,其末端在彼此join_tolerance 内,将被“连接”成一个线段。 |
"edge_min_length" |
短于edge_min_length 的边将在尝试重建表格之前被丢弃。 |
"min_words_vertical" |
当使用"vertical_strategy": "text" 时,至少min_words_vertical 单词必须共享相同的对齐方式. |
"min_words_horizontal" |
使用"horizontal_strategy": "text" 时,至少min_words_horizontal 个词必须共享相同的对齐方式。 |
"intersection_tolerance" ,"intersection_x_tolerance" ,"intersection_y_tolerance" |
当将边组合成单元格时,正交边必须在intersection_tolerance 内才能被认为是相交的点。 |
"text_*" |
然后在从每个发现的表中提取文本时使用以text_ 为前缀的所有设置。Page.extract_text(...) 的所有可能参数在这里也有效。 |
"text_x_tolerance" 、"text_y_tolerance" |
这些text_ -前缀设置也适用于使用text 策略时的table-identification算法。即,当该算法搜索单词时,它会期望每个单词中的单个字母相距不超过text_x_tolerance /text_y_tolerance 点。 |
3、表提取策略
两个vertical_strategy
和horizontal_strategy
都接受以下选项:
策略 | 描述 |
---|---|
"lines" |
使用页面的图形线条——包括矩形对象的边——作为潜在表格单元格的边框。 |
"lines_strict" |
使用页面的图形线条——但不包括矩形对象的边——作为潜在表格单元格的边框。 |
"text" |
对于vertical_strategy :推导连接页面左、右或中心单词的(想象的)线条,并使用这些线条作为潜在表格单元格的边框。对于horizontal_strategy ,相同但使用单词顶部。 |
"explicit" |
仅使用explicit_vertical_lines /explicit_horizontal_lines . |
4、笔记
- 在尝试提取表之前,裁剪一个页面
Page.crop(bounding_box)
通常很有帮助。 - 表提取
pdfplumber
从根本上重新设计v0.5.0
,并引入了突破性的变化。
七、提取表单值
有时,PDF文件可以包含包含人们可以填写和保存的输入的表单。虽然表单字段中的值看起来像PDF文件中的其他文本,但表单数据的处理方式不同。如果您想要血淋淋的详细信息,请参阅本规范的第671页。
pdfplumber
没有处理表单数据的接口,但您可以使用pdfplumber
的pdfminer
包装器访问它。
例如,此片段将检索表单字段名称和值并将它们存储在字典中。
import pdfplumber
from pdfplumber.utils.pdfinternals import resolve_and_decode, resolve
pdf = pdfplumber.open("document_with_form.pdf")
def parse_field_helper(form_data, field, prefix=None):
""" appends any PDF AcroForm field/value pairs in `field` to provided `form_data` list
if `field` has child fields, those will be parsed recursively.
"""
resolved_field = field.resolve()
field_name = '.'.join(filter(lambda x: x, [prefix, resolve_and_decode(resolved_field.get("T"))]))
if "Kids" in resolved_field:
for kid_field in resolved_field["Kids"]:
parse_field_helper(form_data, kid_field, prefix=field_name)
if "T" in resolved_field or "TU" in resolved_field:
# "T" is a field-name, but it's sometimes absent.
# "TU" is the "alternate field name" and is often more human-readable
# your PDF may have one, the other, or both.
alternate_field_name = resolve_and_decode(resolved_field.get("TU")) if resolved_field.get("TU") else None
field_value = resolve_and_decode(resolved_field["V"]) if 'V' in resolved_field else None
form_data.append([field_name, alternate_field_name, field_value])
form_data = []
fields = resolve(resolve(pdf.doc.catalog["AcroForm"])["Fields"])
for field in fields:
parse_field_helper(form_data, field)
运行此脚本后,form_data
一个列表,其中包含每个表单元素的三元素元组。例如,带有城市和州字段的PDF表单可能如下所示。
[
['STATE.0', 'enter STATE', 'CA'],
['section 2 accident infoRmation.1.0',
'enter city of accident',
'SAN FRANCISCO']
]
感谢@jeremybmerrill帮助维护上面的表单解析代码。
八、演示
- 在 California Worker Adjustment and Retraining Notification (WARN) report 上使用
extract_table
。演示基本的可视化调试和表格提取。 - 在 FBI’s National Instant Criminal Background Check System PDFs 上使用
extract_table
。演示如何使用可视化调试来找到最佳的表格提取设置。还演示了Page.crop(...)
和Page.extract_text(...).
- 检查和可视化
curve
对象。 - 从 San Jose PD firearm search report 提取固定宽度数据 ,使用的示例
Page.extract_text(...)
。
九、与其他库的比较
其他几个Python库帮助用户从PDF中提取信息。作为一个广泛的概述,pdfplumber
通过结合以下特性将自己与其他PDF处理库区分开来:
- 轻松访问有关每个PDF对象的详细信息
- 用于提取文本和表格的高级、可定制的方法
- 紧密集成的可视化调试
- 其他有用的实用功能,例如通过裁剪框过滤对象
了解pdfplumber
不提供哪些功能也很有帮助:
- PDF生成
- PDF修改
- 光学字符识别(OCR)
- 强大的支持从OCR文档中提取表格
具体比较
pdfminer.six
提供了pdfplumber
的基础,主要关注解析PDF,分析PDF布局和对象定位,提取文本,不提供表格提取或可视化调试的工具。PyPDF2
是一个纯Python库“能够拆分、合并、裁剪和转换PDF文件的页面。它还可以为PDF文件添加自定义数据、查看选项和密码。”它可以提取页面文本,但不提供对形状对象(矩形、线条等)、表格提取或可视化调试工具的轻松访问。pymupdf
大大快于pdfminer.six
(因此也pdfplumber
),可以生成和修改PDF,但该库需要安装非Python软件(MuPDF)。它也不允许轻松访问形状对象(矩形、线条等),并且不提供表格提取或可视化调试工具。camelot
、tabula-py
和pdftables
都主要关注提取表。在某些情况下,它们可能更适合您尝试提取的特定表。
2024-08-22(四)
一周过得好快