目录
Camelot的介绍和安装
PDF(便携式文档格式)诞生于Camelot 项目,旨在创建“一种跨各种机器配置、操作系统和通信网络交流文档的通用方式”。目标是使这些文档可以在任何显示器上查看并可以在任何现代打印机上打印。PostScript页面描述语言的发明解决了这个问题,它可以创建固定布局的平面文档(封装有文本、字体、图形、图像)。
在较高层次上,PostScript 定义了指令,例如“将此字符放置在平面上的x,y坐标处”。可以通过将角色放置得相对较远来模拟空间。由此延伸,可以通过将字符(构成单词)放置在二维网格中来模拟表格。PDF 查看器只需接受这些指令并绘制所有内容供用户查看。由于PDF只是平面上的字符,因此没有可以提取并用于分析的表格数据结构!
遗憾的是,当今的许多开放数据都被困在 PDF 表格中
1. Camelot介绍
- 可配置性:Camelot 使您可以通过可调整的设置来控制表提取过程。
- 指标:您可以根据准确性和空白等指标丢弃坏表,而无需手动查看每个表。
- 输出:每个表都被提取到pandas DataFrame中,该 DataFrame 无缝集成到ETL 和数据分析工作流程中。您还可以将表导出为多种格式,包括 CSV、JSON、Excel、HTML、Markdown 和 Sqlite。
2.安装
要使用 PyPI 安装 Camelot pip
pip install "camelot-py[base]"
conda是Anaconda发行版的包管理器和环境管理系统。它可用于从conda-forge
通道安装
conda install -c conda-forge camelot-py
来自源代码:
安装依赖项后,您可以通过以下方式从源安装 Camelot
lattice
如果未安装 Ghostscript,该方式将无法运行
git clone https://www.github.com/camelot-dev/camelot cd camelot pip install ".[base]"
它是如何工作的
Stream
Stream可用于解析单格之间有空格的表格来模拟表格结构。它建立在 PDFMiner 的功能之上,即使用边距将页面上的字符分组为单词和句子。
- PDF 页面上的单词根据y轴重叠分为文本行。
- 计算文本边缘,然后用于猜测 PDF 页面上有趣的表格区域。你可以阅读Anssi Nurminen的硕士论文来了解更多关于这个表检测技术的信息。[参见第 20、35 和 40 页]
- 然后猜测每个表区域内的列数。这是通过计算每个文本行中单词数的众数来完成的。基于此模式,选择每个文本行中的单词来计算列x范围的列表。
- 然后,使用当前列x范围内部/外部的单词来扩展当前列列表。
- 最后,使用文本行的y范围和列x范围形成表格,并且在页面上找到的单词根据其x和y坐标分配给表格的单格。
Lattice
格子本质上更具确定性,并且不依赖于猜测。它可用于解析单格之间有分界线的表格,并且可以自动解析页面上存在的多个表格。
它首先使用 Ghostscript 将 PDF 页面转换为图像,然后使用 OpenCV 应用一组形态变换(侵蚀和膨胀)对其进行处理以获得水平和垂直线段。
让我们看看莱迪思如何逐步处理此 PDF的第二页。
快速开始
import camelot
tables = camelot.read_pdf('foo.pdf',flavor='stream')
tables.export('foo.csv', f='csv', compress=True) # json, excel, html
tables.export('foo.xlsx', f='excel', compress=True)#保存为Excel格式
tables[0].to_csv('foo.csv') # 也可以提取成其他格式 to_json, to_excel, to_html
tables[0].df # 获取一个pandas DataFrame!
将表导出为 CSV 文件to_csv()。或者,您可以使用to_json(),或方法分别将表导出为 JSON、Excel、HTML 文件或 sqlite 数据库。to_excel() to_html() to_markdown() to_sqlite()
tables您还可以使用对象的方法一次性导出所有表export()
该export()方法导出带有page-*-table-*
后缀的文件。在上面的示例中,列表中的单个表将导出到foo-page-1-table-1.csv
. 如果列表包含多个表,则会创建多个 CSV 文件。为了避免用多个文件填充您的路径,您可以使用compress=True
,这将在您的路径上创建一个包含所有 CSV 文件的 ZIP 文件。
指定页码
默认情况下,Camelot 仅使用 PDF 的第一页来提取表格。要指定多个页面,可以使用pages
关键字参数:
camelot.read_pdf('your.pdf', pages='1,2,3')
关键字pages
参数接受以逗号分隔的页码字符串形式的页面。您还可以指定页面范围 - 例如,pages=1,4-10,20-30
或pages=1,4-10,20-end
读取加密的 PDF
要从加密的 PDF 文件中提取表格,您必须在调用时提供密码read_pdf()
tables = camelot.read_pdf('foo.pdf', password='userpass') tables <TableList n=1>
Camelot 支持pypdf支持的所有加密类型的 PDF 。这可能需要安装 PyCryptodome。如果无法读取 PDF,则会引发异常。这可能是由于未提供密码、密码不正确或加密算法不受支持。
将来可能会添加进一步的加密支持,但与此同时,如果您的 PDF 文件使用不受支持的加密算法,建议您在调用 之前删除加密read_pdf()。这可以通过QPDF等第三方工具成功实现
处理背景线
为了检测线段,Lattice需要使表格位于前景的线。以下是背景中有线条的表格示例
要处理背景线,您可以通过process_background=True
tables = camelot.read_pdf('background_lines.pdf', process_background=True) tables[1].df
可视化调试
使用可视化调试plot()
需要matplotlib,这是一个可选的依赖项。您可以使用安装它。
pip install camelot-py[plot]
您可以使用该plot()
方法生成处理 PDF 页面时检测到的各种素的matplotlib图。通过调整不同的配置参数,这可以帮助您选择表区域、列分隔符并调试错误的表输出。
您可以使用关键字参数指定要绘制的素的类型kind
。可以通过传递关键字参数将生成的绘图保存到文件中filename
。支持以下绘图类型:
- '文本'
- '网格'
- '轮廓'
- '线'
- '联合的'
- '文本边缘'
'line' 和 'joint' 只能与Lattice一起使用,而 'textedge' 只能与Stream一起使用。
让我们以此PDF为例为每种类型生成一个图
tables = camelot.read_pdf('foo.pdf')
文本
camelot.plot(tables[0], kind='text').show()
正如我们稍后将看到的,这对于Stream记录表区域和列分隔符非常有帮助,以防 Stream 无法正确猜测它们。
当您在图像上移动鼠标光标时,上面显示的xy坐标会发生变化,这可以帮助您记下坐标。
表
让我们绘制表格(看看它是否被正确检测到)。如果未正确检测到表格,此绘图类型以及等高线、线条和接头对于调试和改进提取输出非常有用
camelot.plot(tables[0], kind='grid').show()
表格边界
现在,让我们绘制表格 PDF 页面上出现的所有表格边界。
camelot.plot(tables[0], kind='contour').show()
表格线
让我们绘制表格 PDF 页面上出现的所有线段
camelot.plot(tables[0], kind='line').show()
表格交叉点
最后,让我们绘制表格 PDF 页面上出现的所有线交点。
camelot.plot(tables[0], kind='joint').show()
文本边缘
您还可以通过指定 来可视化页面上找到的文本边缘kind='textedge'
camelot.plot(tables[0], kind='textedge').show()
备注:
此处通过在以上各种方法show的图片上移动鼠标,来确认表格所在坐标。然后以下将通过坐标定位表格
遇到show()的图片,闪一下就没了。
处理方法:
import中增加:import turtle
在show()之后增加 turtle.done()
import turtle turtle.done()
高级用法
指定表区域
在诸如此类的情况下,指定精确的表边界可能会很有用。您可以在此页面上绘制文本并记下表格的左上角和右下角坐标。
read_pdf()您希望 Camelot 分析的表区域可以使用关键字参数作为逗号分隔字符串列表传递给table_areas
。
tables = camelot.read_pdf('table_areas.pdf', flavor='stream', table_areas=['316,499,566,337']) tables[0].df
table_areas
接受 x1,y1,x2,y2 形式的字符串,其中 PDF 坐标空间中的 (x1, y1) -> 左上角和 (x2, y2) -> 右下角。在PDF坐标空间中,页面的左下角为原点,坐标为(0, 0)
指定表范围
其中表格可能不会每次都位于精确的坐标处,而是位于近似区域中。
您可以使用table_regions
关键字参数来read_pdf()解决此类情况。当table_regions
指定时,Camelot只会分析指定的区域来查找表。
tables = camelot.read_pdf('table_regions.pdf', table_regions=['170,370,560,270']) tables[0].df
指定列分隔符
文本彼此非常接近,Camelot 可能会错误地猜测列分隔符的坐标。要纠正此问题,您可以通过在页面上绘制文本来显式指定每个列分隔符的x坐标。
read_pdf()您可以使用关键字参数将列分隔符作为逗号分隔字符串列表传递给columns
。
如果您传递了单列分隔符字符串列表,并且未指定表区域,则分隔符将应用于整个页面。当指定了表区域列表并且还需要指定列分隔符时,两个列表的长度应该相等。每个表区域将使用其索引映射到每个列分隔符的字符串。
例如,如果您指定了两个表区域 ,并且只想为第一个表指定列分隔符,则可以在列分隔符列表中为第二个表传递一个空字符串,如下所示。table_areas=['12,54,43,23', '20,67,55,33']
columns=['10,120,200,400', '']
让我们回到通过绘制此PDF中存在的文本而获得的x坐标,并将表格拿出来
tables = camelot.read_pdf('column_separators.pdf', flavor='stream', columns=['72,95,209,327,442,529,566,606,683']) tables[0].df
由于PDFMiner合并了字符串“NUMBER”、“TYPE”和“DBA NAME”,因此所有这些字符串都被分配给同一单格。让我们在下一节中看看如何解决这个问题。
沿分隔符分割文本
要处理类似上一节输出的情况,您可以传递split_text=True
to read_pdf(),这将拆分位于不同单格但已分配给单个单格的任何字符串
tables = camelot.read_pdf('column_separators.pdf', flavor='stream', columns=['72,95,209,327,442,529,566,606,683'], split_text=True) tables[0].df
标记上标和下标
在某些情况下,您可能想要区分文本和上标或下标,例如此PDF。
在这种情况下,其他工具返回的文本将为24.912
. 当涉及小数点时,这是相对无害的。但当它不存在时,您会想知道为什么数据分析结果会大 10 倍!
您可以通过传递 来解决此问题,它将根据字体大小flag_size=True
将上标和下标括起来,如下所示。<s></s>
tables = camelot.read_pdf('superscript.pdf', flavor='stream', flag_size=True) tables[0].df
从文本中删除字符
您可以使用关键字参数从字符串中删除不需要的字符,例如空格、点和换行符strip_text
。以这个 PDF为例,每行开头的文本包含大量不需要的空格、点和换行符。
tables = camelot.read_pdf('12s0324.pdf', flavor='stream', strip_text=' .\n') tables[0].df
改进猜测表区域
使用Stream时,对于像这样的PDF,自动表格检测可能会失败。这是因为文本在垂直方向上相距相对较远,这可能导致计算的文本边缘较短。
让我们看看默认检测到的表格区域
tables = camelot.read_pdf('edge_tol.pdf', flavor='stream') camelot.plot(tables[0], kind='contour').show()
要改善检测到的区域,您可以增加该edge_tol
值(默认值:50)以抵消垂直方向上距离相对较远的文本的影响。较大edge_tol
将导致检测到更长的文本边缘,从而改进对表格区域的猜测。我们使用值 500。
tables = camelot.read_pdf('edge_tol.pdf', flavor='stream', edge_tol=500) camelot.plot(tables[0], kind='contour').show()
改进猜测的表行
您可以通过传递row_tol=<+int>
将行分组得更紧密,如下所示
tables = camelot.read_pdf('group_rows.pdf', flavor='stream') tables[0].df
检测短线
使用Lattice时可能会出现无法检测到较小线条的情况。检测到的最小线条的大小是通过将 PDF 页面的尺寸除以称为 的缩放因子来计算的line_scale
。默认情况下,其值为 15。
正如您所猜测的, 越大line_scale
,检测到的线条的大小越小
做得line_scale
非常大(>150)将导致文本被检测为线条。
这是一个PDF,其中使用默认值 15 无法检测到分隔标题的小线。
让我们绘制此 PDF 的表格
tables = camelot.read_pdf('short_lines.pdf') camelot.plot(tables[0], kind='grid').show()
显然,无法检测到分隔标题的较小线。让我们尝试使用line_scale=40
,并再次绘制表格
tables = camelot.read_pdf('short_lines.pdf', line_scale=40) camelot.plot(tables[0], kind='grid').show()
在跨单格中移动文本
默认情况下,格子方法会移动跨单格中的文本,首先向左移动,然后向顶部移动,如您在上面的输出表中所观察到的。但是,可以使用关键字参数更改此行为shift_text
。可以将其视为设置桌子的重力- 它决定文本移动并最终静止的方向。
shift_text
需要一个包含以下集合中的一个或多个字符的列表:,然后按 order进行应用。正如我们上面讨论的,默认值是。('', l', 'r', 't', 'b')
['l', 't']
我们将使用上一个示例中的PDF 。Let's pass shift_text=['']
,这基本上意味着文本将经历失重!(它将保留在原处。)
tables = camelot.read_pdf('short_lines.pdf', line_scale=40, shift_text=['']) tables[0].df
这并不奇怪——它确实保留在原处(观察字符串“2400”和“所有可用的个人”)。让我们将重力设置为右下并朝该方向移动文本。shift_text=['r', 'b']
tables = camelot.read_pdf('short_lines.pdf', line_scale=40, shift_text=['r', 'b']) tables[0].df
复制跨单格中的文本
使用Lattice时,您可以在水平或垂直方向或两者上复制跨单格中的文本。默认情况下禁用此行为。
copy_text
需要一个包含以下集合中的一个或多个字符的列表:,然后按 order进行应用。('v', 'h')
让我们在这个PDF上尝试一下。首先,让我们检查输出表,看看是否需要使用任何其他配置参数。
tables = camelot.read_pdf('copy_text.pdf') tables[0].df
我们不需要任何其他东西。现在,让我们copy_text=['v']
在垂直方向复制文本。这可以节省您一些时间,无需在清洁脚本中添加此步骤!
tables = camelot.read_pdf('copy_text.pdf', copy_text=['v']) tables[0].df
调整布局生成
Camelot 建立在 PDFMiner 将页面上的字符分组为单词和句子的功能之上。在某些情况下(例如#170和#215),PDFMiner可以将应该属于同一句子的字符分组到单独的句子中。
为了处理这种情况,您可以调整 PDFMiner 的LAParams kwargs来改进布局生成,方法是将关键字参数作为 dict 使用layout_kwargs
in传递read_pdf()。要了解有关可以调整的参数的更多信息,您可以查看PDFMiner 文档。
tables = camelot.read_pdf('foo.pdf', layout_kwargs={'detect_vertical': False})
使用备用图像转换后端
当使用Lattice时,Camelot 将ghostscript
PDF 页面转换为图像以进行线条识别。如果您遇到安装问题ghostscript
,您可以使用名为 的备用图像转换后端poppler
。您可以指定要使用的图像转换后端:
tables = camelot.read_pdf(filename, backend="ghostscript") # default tables = camelot.read_pdf(filename, backend="poppler")
ghostscript
将被替换为poppler
中的默认图像转换后端v0.12.0
。
如果您同时遇到ghostscript
和问题poppler
,您可以提供自己的图像转换后端:
class ConversionBackend(object): def convert(pdf_path, png_path): # read pdf page from pdf_path # convert pdf page to image # write image to png_path pass tables = camelot.read_pdf(filename, backend=ConversionBackend())
今天的文章
python camelot参数详解--提取PDF指定区域中的表格分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/89014.html