使用pdfplumber进行表格要素提取

一、pdfplumber简介

   参考:PDFPlumber使用入门 - CharyGao - 博客园 (cnblogs.com)

pdfplumber是一个用于处理 PDF 文件的 Python 库,特别擅长从 PDF 文件中提取文本和表格数据。它提供了一种简单而强大的接口,让开发者能够轻松地解析和理解 PDF 内容,而无需深入了解 PDF 文件的底层结构。主要用于获取PDF中的每个文本字符、矩形和行的详细信息,以及可以进行表格提取和可视化调试。

需要注意的是,pdfplumber主要应用于机器生成的pdf,而非扫描的pdf文档。

import pdfplumber
 
with pdfplumber.open("path/to/file.pdf", password='') as pdf:
    first_page = pdf.pages[0]
    print(first_page.chars[0])

可选参数

参数描述
--format [format]csv or jsonjson格式返回更多信息; 它包含PDF级别的元数据(metadata)和每个页面的高度/宽度信息。
--pages [list of pages]一个以空格分隔,以1索引开头的页面或带连字符的页面范围的列表。
例如1,11-15,它将返回第1、11、12、13、14和15页的数据。
--types [list of object types to extract]选择为charannolinecurverectrect_edge
默认为charannolinecurverect

处于最上层的pdfplumber.PDF类表示单个PDF,并且具有两个主要属性:

属性描述
.metadata从PDF的Info中获取元数据键 /值对字典。 通常包括“ CreationDate”,“ ModDate”,“ Producer”等。
.pages一个包含pdfplumber.Page实例的列表,每一个实例代表PDF每一页的信息。

1、pdfplumber.Page类

pdfplumber.Page类是pdfplumber整个的核心,大多数操作都围绕这个类进行操作,它具有以下几个属性:

属性描述
.page_number页码顺序,从第一页的1开始,第二页为2,依此类推。
.width页面宽度
.height页面高度
.objects/.chars/.lines/.rects/
.curves/.figures/.images
这些属性中的每一个都是一个列表,并且每个列表针对嵌入面上的每个此类对象包含一个字典。 有关更多详细信息,请参见下面的"对象(Object)"。

以及这些主要的方法(method):

方法描述
.crop(bounding_box)返回裁剪后的页面,该bouding_box(边界框)应表示为具有值(x0, top, x1, bottom)的4元组。 裁剪后的页面保留了至少部分位于边界框内的对象。 如果对象仅部分落在该框内,则也会被涵盖。
.within_bbox(bounding_box).crop相似,但是只会包含完全在bounding_box内的部分。
.filter(test_function)返回仅包含.objects的页面版本,该对象的test_function(obj)返回True
.extract_text(x_tolerance=0, y_tolerance=0)将页面的所有字符对象整理到一个字符串中。
若其中一个字符的x1与下一个字符的x0之差大于x_tolerance,则添加空格。
若其中一个字符的doctop与下一个字符的doctop之差大于y_tolerance,则添加换行符。
.extract_words(x_tolerance=0, y_tolerance=0, horizontal_ltr=True, vertical_ttb=True)返回所有单词外观及其边界框的列表。字词被认为是字符序列,其中(对于“直立”字符)一个字符的x1和下一个字符的x0之差小于或等于x_tolerance,并且一个字符的doctop和下一个字符的doctop小于或等于y_tolerance。对于非垂直字符也采用类似的方法,但是要测量它们之间的垂直距离,而不是水平距离。
参数horizontal_ltrvertical_ttb指示是否应从左到右(对于水平单词)/从上到下(对于垂直单词)读取字词。
.extract_tables(table_settings)从页面中提取表格数据。 有关更多详细信息,请参见下面的“表格抽取”。
.to_image(**conversion_kwargs)返回PageImage类的实例。 有关更多详细信息,请参见下面的“可视化调试”。 有关conversion_kwargs,请参见此处

对于每一个pdfplumber.PDFpdfplumber.Page的实例都提供了对4种对象操作的方法。以下属性均返回所对应对象的Python列表

  • .chars 代表每一个独立的字符;
  • .annos 代表注释里的每一个独立的字符;
  • .lines 代表一个独立的一维的线;
  • .rects 代表一个独立的二维的矩形;
  • .curves(弯曲,曲线) 代表一系列连接的点;
  • .images 代表一个图像;

每一个对象用一个Python词典dict进行表示,具有以下属性:

chars / annos 属性

属性描述
page_number找到此字符的页码。
text字符文本,如"z"、“Z"或者"你”。
fontname字符的字体。
size字号。
adv等于文本宽度字体大小缩放因子。
upright字符是否是直立的。
height字符高度。
width字符宽度。
x0字符左侧到页面左侧的距离。
y0字符底部到页面底部的距离。
x1字符右侧到页面左侧的距离。
y1字符顶部到页面底部的距离。
top字符顶部到页面顶部的距离
bottom字符底部到页面顶部的距离
doctop字符顶部到文档顶部的距离。
obj_type"char""anno"

line 属性

属性描述
page_number找到此线的页码。
height线的高度。
width线的宽度。
x0线的最左侧到页面左侧的距离。
y0线的底部到页面底部的距离。
x1线的最右侧到页面左侧的距离。
y1线的顶部到页面底部的距离。
top线的顶部到页面顶部的距离。
bottom线的底部到页面顶部的距离。
doctop线的顶部到文档顶部的距离。
linewidth线的粗度。
obj_type"line"

rect 属性

属性描述
page_number找到此矩形的页码。
height矩形的高度。
width矩形的宽度。
x0矩形的最左侧到页面左侧的距离。
y0矩形的底部到页面底部的距离。
x1矩形的最右侧到页面左侧的距离。
y1矩形的顶部到页面底部的距离。
top矩形的顶部到页面顶部的距离。
bottom矩形的底部到页面顶部的距离。
doctop矩形的顶部到文档顶部的距离。
linewidth矩形边框的粗度。
obj_type"rect"

curve 属性

属性描述
page_number找到此曲线的页码。
points点,作为(x,top)元组的列表,用以描述曲线。
height曲线bounding_box的高度。
width曲线bounding_box的宽度。
x0曲线的最左侧点到页面左侧的距离。
y0曲线最底部点到页面底部的距离。
x1曲线的最右侧点到页面左侧的距离。
y1曲线最顶部点到页面底部的距离。
top曲线最顶部的点到页面顶部的距离。
bottom曲线最底部点到页面顶部的距离。
doctop曲线最顶部点到文档顶部的距离。
linewidth连线的粗度。
obj_type"curve"

此外,pdfplumber.PDFpdfplumber.Page都提供对两个派生对象列表的访问:.rect_edges(将每个矩形分解成四行)和.edges(将.rect_edges.lines组合)。

2、可视化调试

 1)使用matplotlib绘图

import pdfplumber
from PIL import Image, ImageDraw, ImageFont
import matplotlib.pyplot as plt

def visualize_page(page, output_path=None):
    # 创建一个图像对象,与PDF页面大小相同
    img = Image.new('RGB', (int(page.width), int(page.height)), color='white')
    draw = ImageDraw.Draw(img)
    
    # 绘制页面上的所有文字
    for word in page.extract_words():
        draw.text((word['x0'], word['top']), word['text'], fill='black')
    
    # 如果存在表格,绘制表格的边框
    tables = page.find_tables(
        table_settings={
            "vertical_strategy": "lines",
            "horizontal_strategy":"lines"
    })
    for table in tables:
        for edge in table.edges:
            draw.line([edge[0], edge[1]], fill='red', width=2)
    
    # 显示图像
    plt.imshow(img)
    plt.axis('off')
    plt.show()
    
    # 如果提供了输出路径,保存图像
    if output_path:
        img.save(output_path)

# 使用pdfplumber打开PDF
with pdfplumber.open("path_to_your_pdf.pdf") as pdf:
    # 选择要可视化的页面
    page = pdf.pages[0]
    
    # 调用可视化函数
    visualize_page(page, output_path="output_image.png")

 2)使用ImageMagick+ghostscipt

:使用pdfplumber的可视化调试工具需要额外用到两个工具

使用.to_image()创建PageImage

要将任何页面(包括裁剪的页面)转换为PageImage对象,请调用my_page.to_image()。 您可以选择传递resolution = {integer}关键字参数,默认为72。例如:

im = my_pdf.pages[0].to_image(resolution=150)

PageImage对象可以在IPython / Jupyter notbook上很好地展示,它们自动呈现为单元格输出。 例如:

PageImage输出

基础PageImage方法
方法描述
im.reset()清除到目前为止已绘制的所有内容。
im.copy()将图像复制到新的PageImage对象
im.save(path_or_fileobject, format="PNG")保存带注释的图像。
绘图方法

你可以将显式坐标或任何pdfplumber.PDF对象(例如,char,line,rect)传递给这些方法。

单一操作批量操作描述
im.draw_line(line, stroke={color}, stroke_width=1)im.draw_lines(list_of_lines, **kwargs)linecurve或两个2元组绘制一条线(例如((x,y), (x, y)))。
im.draw_vline(location, stroke={color}, stroke_width=1)im.draw_vlines(list_of_locations, **kwargs)location的x坐标处绘制一条垂直线。
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)rectchar等或4元组边界框绘制一个矩形。
im.draw_circle(center_or_obj, radius=5, fill={color}, stroke={color})im.draw_circles(list_of_circles, **kwargs)(x, y)坐标或charrect等的中心处绘制一个圆。

注意:上面的方法是基于Pillow的ImageDraw方法构建的,但是已经对参数进行了调整,以与SVG的fill/stroke/stroke_width命名法保持一致。

3、表格抽取

pdfplumber的表检测方法大量借鉴了Anssi Nurminen的硕士学位论文(可能需要FQ阅读),并受到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)方法的访问。
.extract_tables(table_settings={})返回从页面上找到的所有表中提取的文本,并以结构table -> row -> cell的形式表示为列表列表的列表。
.extract_table(table_settings={})返回从页面上最大的表中提取的文本,以列表列表的形式显示,结构为row -> cell。 (如果多个表具有相同的大小——以单元格的数量来衡量——此方法将返回最接近页面顶部的表。)
.debug_tablefinder(table_settings={})返回TableFinder类的实例,可以访问.edges.intersections.cells.tables属性。

表格抽取设置

默认情况下,extract_tables使用页面的垂直和水平线(或矩形边缘)作为单元格分隔符。 但是该方法可以通过table_settings参数进行高度自定义。 可能的设置及其默认值:

table_settings={
    "vertical_strategy": "lines",
    "horizontal_strategy": "lines",
    "explicit_vertical_lines": [],
    "explicit_horizontal_lines": [],
    "snap_tolerance": 3,
    "join_tolerance": 3,
    "edge_min_length": 3,
    "min_words_vertical": 3,
    "min_words_horizontal": 1,
    "keep_blank_chars": False,
    "text_tolerance": 3,
    "text_x_tolerance": None,
    "text_y_tolerance": None,
    "intersection_tolerance": 3,
    "intersection_x_tolerance": None,
    "intersection_y_tolerance": None,
}
设置描述
"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_tolerance像素内的平行线将被“捕捉”到相同的水平或垂直位置。
"join_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个单词必须共享相同的对齐方式。
"keep_blank_chars"使用text策略时,将" "字符作为单词的一部分而不是单词分隔符。
"text_tolerance",
"text_x_tolerance",
"text_y_tolerance"
text策略搜索单词时,它将期望每个单词中的各个字母相差不超过text_tolerance像素。(tolerance:容忍)
"intersection_tolerance",
"intersection_x_tolerance",
"intersection_y_tolerance"
将边缘合并为单元格时,正交边缘必须在intersection_tolerance像素内才能被视为相交。

表格抽取策略

vertical_strategyhorizontal_strategy都接受以下选项:

策略描述
"lines"使用页面的图形线(包括矩形对象的边)作为潜在表格单元格的边界。
"lines_strict"使用页面的图形线(而不是矩形对象的边)作为潜在表格单元格的边界。
"text"对于vertical_strategy:推导连接页面上单词的左,右或中心的(虚构)线,并将这些线用作潜在的表格单元格的边界。
对于horizontal_strategy:相同,但使用顶部的单词。
"explicit"仅使用在explicit_vertical_lines / explicit_horizontal_lines中显式定义的行。
注意
  • 在尝试提取表之前,裁剪页面通常很有帮助Page.crop(bounding_box)

  • pdfplumber的表提取已针对v0.5.0进行了彻底的重新设计,并引入了很多显著更新。

 二、表格处理

1、表格提取

cur_table = page.extract_tables()

2、表格合并

1)获取表格边界

2)获取文本边界

3)若上一页表格边界 > 文本边界 且 当前页表格边界 < 文本边界,则合并表格

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值