还在用收费的工具处理PDF?用Python助力冲破会员牢笼

前言
害,又遇到了要处理 PDF 文件的活了,需求是提取表格,还要拜托有某软件会员的同事操作一下,没想到会员早就过期了。毕竟不常用,为了个 PDF 文件充值,大可不必。就凭我的码字速度,在输入支付密码之前就能把内容都敲下来。
但转念一想,PDF 的自动化需求种类还是很多的,比如增删改页面、提取文字和图像等等,万一哪天遇到大批量操作的需求可就麻烦了。自动化之王(不是)Python 肯定有办法!让我们来瞅瞅吧。

一、运用实例

先看看笔者遇到的问题,要处理的是这样一份 PDF 文件,前几页为文字,文末附了几个表格,其中有一个表格是跨页的。那要如何提取表格呢?
在这里插入图片描述
核心代码仅需两行,打开 PDF 文件,读取表格即可,十分便利。

import pdfplumber

pdf = pdfplumber.open("test.pdf")
for i in range(2, 4):
    tables = pdf.pages[i].extract_tables()
    for t in tables:
        print(t)

从输出结果可见,是一种嵌套列表的形式,而一个表格跨页后的部分会被识别为另外的表格。

[[‘序号’, ‘标题 1’, ‘标题 2’, ‘标题 3’], [‘1’, ‘内容’, ‘内容’, ‘内容’], [‘2’, ‘内容’, ‘内容’, ‘内容’], [‘3’, ‘内容’, ‘内容’, ‘内容’]]
[[‘序号’, ‘字段 1’, ‘字段 2’, ‘字段 3’, ‘字段 4’, ‘字段 5’], [‘1’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘2’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘3’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘4’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘5’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’]]
[[‘6’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘7’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘8’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘9’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘10’, ‘内容’, ‘内容’, ‘内容’, ‘内容’, ‘内容’]]
[[‘序号’, ‘表头 1’, ‘表头 2’, ‘表头 3’, ‘表头 4’], [‘1’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘2’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘3’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘4’, ‘内容’, ‘内容’, ‘内容’, ‘内容’], [‘5’, ‘内容’, ‘内容’, ‘内容’, ‘内容’]]

那么接下来就用传说能让 EXCEL 飞起来的 xlwings 库将数据写入吧!对代码稍加改造,详见注释。

import pdfplumber
import xlwings as xw


wb = xw.books.active  # 连接到活动的工作簿
sht = wb.sheets['Sheet1']  # 连接子表1
n = 1
pdf = pdfplumber.open("test.pdf")  # 打开PDF
for i in range(2, 4):
    tables = pdf.pages[i].extract_tables()  # 获取第3、4页的表格
    for t in tables:
        for row in t:
            if '序号' in row and n>1:
                n += 1  # 不同表格之间隔一行
            sht.range(f'A{n}').value = row  # 将表格的一行从A1单元格开始写入
            n += 1  # 换行

果然起飞,这种看得见的效果,相比其他在内存中执行完才存表的 EXCEL 处理库要更加过瘾。 还可以利用 xlwings 对各个表格添加边框、加粗表头等,在此不做赘述,有兴趣的同学可以自行探索,本文的主角可是 PDF 处理库啊,下面有请——
在这里插入图片描述

二、pdfplumber 与 PyPDF2

pdfplumber 是一个基于 pdfminer.six 的 PDF 内容抽取工具,如前文的体验一般,操作十分简单,可惜专攻识别而无法直接对 PDF 做修改保存。如果有更复杂的需求,可以学习 pdfminer,但需要对 PDF 文件模型有所了解,学起来有一定的难度。

官网:https://github.com/jsvine/pdfplumber

PyPDF2 也是一个为人所熟知的 PDF 页面级处理工具,它更加擅长对页面进行分割、合并、裁剪和转换等操作,而对内容的提取能力欠缺。

官网:https://pythonhosted.org/PyPDF2

就让我们从常用的 PDF 操作来认识这两位吧!

1、插入/增加页

我们先另外再准备一个 PDF 文件,用作插入试验。
在这里插入图片描述
PyPDF2 通过读写器来操作 PDF 文件,假如要在第 1 页之后插入页面,需要通过 Reader 对象的 getPage 方法获取页面,再通过 Writer 对象的 addPage 方法来添加页面,最后写入文件流。

from PyPDF2 import PdfFileWriter, PdfFileReader


output = PdfFileWriter()
input = PdfFileReader(open("test.pdf", "rb"))
insert = PdfFileReader(open("insert.pdf", "rb"))

for i in range(0, 4):
    output.addPage(input.getPage(i))
    if i ==0:
        output.addPage(insert.getPage(0))

output.write(open("output.pdf", "wb"))

在这里插入图片描述

2、选取/删除页

通过插入页的方式,相信大家已经能想到选取/删除页的办法了,那就是选择需要的 page 索引来 addPage。比如对上一步的 output.pdf 仅保留第 1 和第 3 页.

from PyPDF2 import PdfFileWriter, PdfFileReader


new_output = PdfFileWriter()
input = PdfFileReader(open("output.pdf", "rb"))

for i in range(0, 4):
    if i==0 or i==2:
        new_output.addPage(input.getPage(i))

new_output.write(open("new_output.pdf", "wb"))

在这里插入图片描述

3、提取文本信息

不得不说,pdfplumber 封装得很精炼,用非常直观的语句就能实现需求,通过页面对象的 extract_text 方法即可提取文本。要注意的是,pdfplumber 只能提取原生内容,如果是图片,则需要另外借助 OCR 工具。

import pdfplumber

pdf = pdfplumber.open("test.pdf")
pages = pdf.pages
for p in pages:
    print(p.extract_text())

1
1
1
2
2
2
3
3
3
表一
序号 标题 1 标题 2 标题 3
1 内容 内容 内容
2 内容 内容 内容
3 内容 内容 内容
表二

4、提取表格信息

之前笔者用 extract_tables() 批量提取了表格,我们还可以 extract_table() 提取指定页面的表格,然后可将其装载进 DataFrame 以便后续分析操作。

import pdfplumber
import pandas as pd


pdf = pdfplumber.open("test.pdf")
table = pdf.pages[2].extract_table()
df = pd.DataFrame(table[1:], columns=table[0])
print(df)

在这里插入图片描述
但是却发现它提取的是该页面的表二,难道默认不是提取页面中第一个表吗?
在这里插入图片描述

经查阅官方资料得知:extract_table() 返回页面上最大的表,如果多个表的大小相同(以单元格数衡量),则返回最靠近页面顶部的表。
在这里插入图片描述

5、提取图片信息

下面我们再准备一个带图片的 PDF 文件,用作提取试验。
在这里插入图片描述
依照官方提示,想进行可视化操作还需要安装两个依赖工具。(此外,笔者发现还有其他库能提取 PDF 的图片元素或转化 PDF 页面为图片,如 PyMuPDF、pdf2image等,可以拓展学习)
在这里插入图片描述
安装地址如下,注意要选择和 python 对应位数的版本。

https://imagemagick.org/script/download.php#windows
https://ghostscript.com/download/gsdnld.html

通过页面对象的 images 属性即可获取图像参数列表,在此我们只有一张图,获取其图像流数据并写入文件,成功提取图片。

import pdfplumber


pdf_pic = pdfplumber.open("picture.pdf")
page = pdf_pic.pages[0]
img = page.images
data = img[0]['stream'].get_data()
with open('pic.png', 'wb') as f:
    f.write(data)

在这里插入图片描述
同时我们也可以验证一下对图片中文字和表格的提取,发现是不可行的。

print(page.extract_text())
print(page.extract_table())

我是文字
None

6、页面转换成图片

除了可以提取页面中的图片元素,也可以将整个页面转成图片输出,我们换回最初的那一份 PDF 来试验。页面对象具有 to_image 方法,比单个元素的操作更加简单。

import pdfplumber


pdf = pdfplumber.open("test.pdf")
pages = pdf.pages
for i, p in enumerate(pages):
    p.to_image().save(f"第{i+1}页.png", format="PNG")

在这里插入图片描述
之后我们还可以将多个图片连接成长图,适合移动端浏览。

from os import listdir
from PIL import Image


imgs = [Image.open(f) for f in listdir('.') if f.endswith('.png')]  # 获取当前目录下的图像
width, height = imgs[0].size  # 单幅图像尺寸
result = Image.new(imgs[0].mode, (width, height * len(imgs)))  # 创建空白长图

for i, im in enumerate(imgs):  # 拼接图片
    result.paste(im, box=(0, i * height))

result.save('长条图.png')

在这里插入图片描述

三、小结

本篇通过 1 个实际案例和 6 个小操作演示了两个 PDF 库的基本功能,还有很多隐藏的属性等待我们去探索,比如添加水印、加/解密等,这还不赶快自己开发一个 PDF 工具去收费?暴富指日可待!这里是妄图暴富的 Seon塞翁,下一篇见。

  • 9
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值