Python 操作PDF文档
1、PDF
(便携式文件格式,Portable Document Format)是由Adobe Systems在1993年用于文件交换所发展出的文件格式。 PDF主要由三项技术组成:衍生自PostScript;字型嵌入系统;资料压缩及传输系统。它的优点在于跨平台、能保留文件原有格式(Layout)、开放标准,能免版税(Royalty-free)自由开发PDF相容软体,是一个开放标准,2007年12月成为ISO 32000国际标准。
PDF格式的主要优点:
跨平台性:PDF文件格式与操作系统平台无关,文件不管是在 Windows,Unix还是在苹果公司的 Mac OS 操作系统中都是通用的。
易于阅读:不同平台、不同阅读软件打开不会出错或变形,以及转换成PDF后可以避免其他软件产生的不兼容和字体替换问题,使得文档的灵活性提高。
不易编辑:PDF是板式文档,可防止他人无意中触到键盘修改文件内容。
体积小巧:PDF文件使用了工业标准的压缩算法,通常比 PostScript 文件小,易于传输与储存。
信息丰富:PDF文件格式可以将文字、字型、格式、颜色及独立于设备和分辨率的图形图像等封装在一个文件中。该格式文件还可以包含超文本链接、声音和动态影像等电子信息。
1.1. Python处理PDF常用类库
注意: 2023年,都已经回归到 pypdf 了。 在 Debian 12中,通过下面命令安装:
sudo apt install -y python3-pypdf
大多数组织以PDF形式发布其数据。随着AI的发展,我们需要更多数据来进行预测和分类。 PDF处理有些困难,但是我们可以利用下面的API来简化它。 本文将简要介绍如何使用Python处理PDF。Python处理PDF的第三库也有很多,主要有:
PDFMiner Python中用于PDF处理的库。容易安装,也容易使用。PDFMiner为非程序员提供命令实用程序,为程序员提供API接口。
PyPDF 这个Python PDF库是可扩展的。可以从pdf中提取文本,裁剪,然后将PDF文档与加密和解密功能合并。PyPDF有很多版本。在PyPDF4之前,PyPDF2更加流行。目前都已经合并到 pypdf .
pdfrw 与上面两个提及非常相似。除了这种相似性之外,pdfrw还具有自己的USP(唯一卖点)。pdfrw:一个替代的PDF操作包。Patrick Maupin创建了一个名为pdfrw的软件包,它可以完成许多与PyPDF2相同的工作。除了加密的特殊情况外,本文后面提到PyPDF2的所有操作,pdfrw均可以实现。pdfrw的最大区别在于它与ReportLab软件包集成,因此你可以使用一些或所有预先存在的PDF构建一个新的PDF。
Slate 它是PDFMiner的包装实现。
reportlab Reportlab的特长在于创建PDF文件,尤其是程序动态生成PDF文件的功能十分强大,但是遗憾的是开源版本没有提供读取PDF文件的相关功能。
pyMuPDF MuPDF可以访问PDF,XPS,OpenXPS,CBZ,EPUB和FB2(电子书)格式的文件,并且以其最佳性能和高渲染质量而著称。
tabula-py tabula 是专门用来提取PDF表格数据的,同时支持PDF导出为CSV、Excel格式,但是这工具是用 java 写的,依赖 java7/8。tabula-py 就是对它做了一层 python 的封装
pdfplumber pdfplumber 是按页来处理 pdf 的,可以获得页面的所有文字,并且提供的单独的方法用于提取表格。
1.2. PyPDF
pyPdf,PyPDF2和PyPDF4的历史
最初的pyPdf软件包于2005年发布。pyPdf的最后一个正式版本是在2010年。 大约一年后,一家名为Phasit的公司赞助了一个名为PyPDF2的pyPdf分支。 该代码编写为向后与原始代码兼容,并且用了好多年,效果一直很好,其最后一个版本是在2016年。
有一个名为PyPDF3的软件包简短系列版本,然后该项目被重命名为PyPDF4。 所有这些项目都完全相同,但pyPdf和PyPDF2 +之间的最大区别在于后者版本增加了Python 3支持。 Python 3的原始pyPdf有一个不同的Python 3分支,但是这个分支已经多年没有维护了。
最新版本的PyPDF4支持PyPDF2的大多数功能,但也有部分功能不兼容。
pypdf4主要功能
PyPDF4是一个纯Python PDF库,能够拆分,合并,裁剪和转换PDF文件的页面。它还可以将自定义数据,查看选项和密码添加到PDF文件。它可以从PDF检索文本和元数据,以及将整个文件合并在一起。
提取文档信息(标题,作者等)
逐页拆分文档
逐页合并文档
裁剪页面
将多个页面合并为一个页面
加密和解密PDF文件
2. 读取PDF
接下来使用PyPDF4对PDF文件进行读取,需要注意的是他对英文的支持比较好,如果读取中文就会出现乱码等问题,在后面我们会介绍几个支持读取中文的库。
2.1. 查看PDF信息
可以在自己的电脑上随便找一个PDF文件进行尝试操作。
from pypdf import PdfReader
with open('minimal.pdf','rb') as f:
pdf=PdfReader(f)
information=pdf.metadata
number_of_pages=len(pdf.pages)
txt=f"""Author: {information.author}
Creator: {information.creator}
Producer: {information.producer}
Subject: {information.subject}
Title: {information.title}
Number of pages: {number_of_pages}"""
print(txt)
Author: None
Creator: cairo 1.11.2 (http://cairographics.org)
Producer: cairo 1.11.2 (http://cairographics.org)
Subject: None
Title: None
Number of pages: 1
PdfFileReader是一个具有多种与PDF文件交互的方法的类。 在此示例中,调用了 .getDocumentInfo() ,它将返回DocumentInformation的实例,包含了我们感兴趣的大部分信息。 我们还可以在reader对象上调用 .getNumPages() ,让它返回文档中的页数。
information这个变量具有多个实例属性,可以使用这些属性从文档中获取所需的其余元数据。我们可以打印出该信息并将其返回以备将来使用。
2.2. 读取文本
我们先打开PDF文件,再查看他的页数。
pdffile = open('postgis-essential-0423.pdf','rb')
pdfreader = PdfReader(pdffile)
print(len(pdfreader.pages))
202
我们查看第二页的信息。
page = pdfreader.pages[0]print(page.extract_text().strip())
第 1章地理空间数据库的发展、技术与标准
回答地理空间问题有很多工具,或桌面应用系统。这种方法虽然功能完备,但不能同时回
答许多问题。此外,这种方法通常无法在一个数据集中有效地管理和操作大量的空间数据集,
也无法使任务自动化。
一旦需要可伸缩性、对大型数据集的支持以及直接输入机制,大多数用户就会使用空间数
据库进行探索。有几个可用的空间数据库软件,一些是专有的,另一些是开源的。 PostGIS 是
一个开源的空间数据库软件,可能是所有空间数据库软件中最容易访问的。
PostGIS 作为扩展运行,为 PostgreSQL 数据库提供空间功能。在这种能力下, PostGIS
允许将空间数据与常规关系型数据一起包含进来。通过构建 PostGIS 提供的核心功能和
PostgreSQL 固有的可扩展性,可以实现新的或增强的功能。
在数据库存储方面,数据库是高级形式,而 PostGIS 赋予其更多的功能。
1.1平面文件、空间数据引擎到空间数据库
在传统的第一代 地理信息系统 (GIS)实现中,所有的 空间数据 都存储在 平面文件( flat
files)中,需要专门的 GIS软件来解释和操作这些数据。这些第一代管理系统旨在满足用
户的需求,其中所有所需的数据都在用户的组织领域中。它们是专为处理 空间数据 而构建的专
有的、独立的系统,应用程序和平面文件之间的耦合性非常高,平面文件里的空间数据没有数
据独立性。
为了提高数据库管理系统( DBMS)对空间数据的管理能力 ,国内外较为流行的主要集中在
"关系型数据库 +空间数据引擎" 、 "扩展对象关系型数据库"两方面。
"关系型数据库+空间数据引擎" 技术方案访问迅速,与 GIS联系紧密,在应用中占有一
定优势。问题是引擎与数据库内核独立,难以利用数据库系统中已有的成熟的管理、访问技术,
在进一步发展上有致命弱点。
"扩展对象空间数据库系统" 技术方案从理论上来看,是最适用于空间数据的表达和管理
的。
3
idx_arr = [0]
for idx, page in enumerate(pdfreader.pages):
cnts = page.extract_text().strip().splitlines()
print(idx, cnts[0])
if '第' in cnts[0] and '章' in cnts[0]:
print(idx)
print(cnts[0])
if idx != 0:
idx_arr.append(idx)
idx_arr.append(len(pdfreader.pages))
print(idx_arr)
0
第 1章地理空间数据库的发展、技术与标准
12
第 2章创建您的第一个空间数据库
22
第 3章 PostGIS 的数据读写与转换
36
第 4章 PostGIS 中的几何图形:文本表达的输入与输出
50
第 5章几何图形的简单性与有效性
64
第 6章合成和分解几何图形
78
第 7章空间数据坐标系统与投影
84
第 8章空间度量与测度
88
第 9章使用 Geography
96
第 10章一元几何图形操作
102
第 11章空间关系
114
第 12章矢量数据空间操作:二元算子
128
第 13章空间索引
148
第 14章栅格数据读写:导入与导出
158
第 15章使用栅格数据
168
第 16章管理栅格数据
180
第 17章高级话题
190
第 18章运维
[0, 12, 22, 36, 50, 64, 78, 84, 88, 96, 102, 114, 128, 148, 158, 168, 180, 190, 202]
from pypdf import PdfWriter
pdf_idx = 1
from pathlib import Path
outws = Path('xx_post')
if outws.exists():
pass
else:
outws.mkdir()
for qq, hh in zip(idx_arr[:-1], idx_arr[1:]):
outfile = outws / f'xx_{pdf_idx:02}.pdf'
print(outfile)
merger = PdfWriter() # add the first 3 pages of input1 document to output merger.append(fileobj=pdfreader, pages=(qq, hh)) # insert the first page of input2 into the output beginning after the second page # merger.merge(position=2, fileobj=input2, pages=(0, 1)) # append entire input3 document to the end of the output document # merger.append(input3) # Write to an output PDF document output = open(outfile, "wb") merger.write(output) # Close File Descriptors merger.close() output.close() pdf_idx = pdf_idx + 1
xx_post/xx_01.pdf
xx_post/xx_02.pdf
xx_post/xx_03.pdf
xx_post/xx_04.pdf
xx_post/xx_05.pdf
xx_post/xx_06.pdf
xx_post/xx_07.pdf
xx_post/xx_08.pdf
xx_post/xx_09.pdf
xx_post/xx_10.pdf
xx_post/xx_11.pdf
xx_post/xx_12.pdf
xx_post/xx_13.pdf
xx_post/xx_14.pdf
xx_post/xx_15.pdf
xx_post/xx_16.pdf
xx_post/xx_17.pdf
xx_post/xx_18.pdf
4.2.3. 读取图片
PDF有扫描的图片,或者插入图片在内,首先验证文档是否加密。
pdffile = open('servers.pdf','rb')
pdfreader.is_encrypted
False
输出为False,即为没有加密过。
pg = pdfreader.pages[0]pg.keys()
dict_keys(['/Resources', '/Contents', '/Parent', '/Type', '/MediaBox'])
图片存储在 ['/Resources']['/XObject'] 里,通过type查看发现 pg['/Resources']['/XObject']['/Im1'] 是一个EncodedStreamObject,通过 getData() 方法可以获取它的数据,直接以二进制模式写入文件即可保存。再使用前面使用的pillow库查看
pg.values()dict_values([IndirectObject(322, 0, 140697627366992), [IndirectObject(321, 0, 140697627366992)], IndirectObject(1646, 0, 140697627366992), '/Page', [0, 0, 595.27999999999997, 841.88999999999999]])
pg['/Resources']['/XObject']
KeyError Traceback (most recent call last)
Cell In [9], line 1
----> 1 pg['/Resources']['/XObject']
File /usr/lib/python3/dist-packages/pypdf/generic/_data_structures.py:269, in DictionaryObject.getitem (self, key)
268 def getitem (self, key: Any) -> PdfObject:
--> 269 return dict.getitem(self, key).get_object()
KeyError: '/XObject'
pg['/Resources']['/XObject']['/FXX1']
im8 = pg['/Resources']['/XObject']['/FXX1'].get_data()
with open('im.png', 'wb') as f:
f.write(im8)
from matplotlib import pyplot as plt
from PIL import Image
img=Image.open('./im.png')
plt.imshow(img)
plt.show()
KeyError Traceback (most recent call last)
Cell In [11], line 1
----> 1 im8 = pg['/Resources']['/XObject']['/FXX1'].get_data()
2 with open('im.png', 'wb') as f:
3 f.write(im8)
File /usr/lib/python3/dist-packages/pypdf/generic/_data_structures.py:269, in DictionaryObject.getitem (self, key)
268 def getitem (self, key: Any) -> PdfObject:
--> 269 return dict.getitem(self, key).get_object()
KeyError: '/XObject'