进阶PDF,就用Python(PyPDF2模块)

python

PDF文件被广泛应用于工作和生活中,下载资料和传递只读文件都必不可少。今天我们就来详解Python在PDF中的应用。


对于PDF文件,常见的需求也就是两类:

  • 处理文件本身,属于文件页面级操作,如合并/分拆PDF页面、加/解密、加/去水印;

  • 处理文件内容,属于内容级操作,如提取文字、表格数据、图表等。


目前Python用于处理PDF的模块,主要有3个:

  • PyPDF2:模块成熟,最后一次更新在2年前,适合页面级操作,文字提取效果较差。

  • PDFMiner:擅长文字抽取,目前主分支已停止维护,取而代之的是Pdfminer.six

  • Pdfplumber:基于pdfminer.six的文本内容抽取工具,使用门槛更低,如支持表格提取。


实战中,可以根据需求的类型选择模块。如果是页面级的操作,就用PyPDF2。如果需要内容抽取,优先使用pdfplumber

对应的模块安装:

  • pip install pypdf2

  • pip install pdfminer.six

  • pip install pdfplumber


PyPDF2


PyPDF2的主要能力在页面级操作,比如:

  • 获取PDF文档基本信息

  • PDF分割及合并

  • PDF的旋转及排序

  • PDF加水印及去水印

  • PDF加密及解密


PyPDF2的核心两个类是PdfFileReader和PdfFileWriter,完成PDF文件的读写操作。


获取PDF文档基本信息


import pathlib

from PyPDF2 import PdfFileReader

path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf')

f_path = path.joinpath('2020-新冠肺炎疫情对中国连锁餐饮行业的影响调研报告-中国连锁经营协会.pdf')

with open(f_path, 'rb') as f:

   pdf = PdfFileReader(f)

   info = pdf.getDocumentInfo()

   cnt_page = pdf.getNumPages()

   is_encrypt = pdf.getIsEncrypted()

print(f'''

作者: {info.author}

创建者: {info.creator}

制作者: {info.producer}

主题: {info.subject}

标题: {info.title}

总页数: {cnt_page}

是否加密: {is_encrypt}

''')


PDF分割及合并


import pathlib

from PyPDF2 import PdfFileReader, PdfFileWriter

path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf')

f_path = path.joinpath('2020-新冠肺炎疫情对中国连锁餐饮行业的影响调研报告-中国连锁经营协会.pdf')

out_path = path.joinpath('002pdf_split_merge.pdf')

out_path_1 = path.joinpath('002pdf_split_half_front.pdf')

out_path_2 = path.joinpath('002pdf_split_half_back.pdf')

# 把文件分为两半

with open(f_path, 'rb') as f, open(out_path_1, 'wb') as f_out1, open(out_path_2, 'wb') as f_out2:

   pdf = PdfFileReader(f)

   pdf_out1 = PdfFileWriter()

   pdf_out2 = PdfFileWriter()

   cnt_pages = pdf.getNumPages()

   print(f'共 {cnt_pages} 页')

   for i in range(cnt_pages):

       if i <= cnt_pages //2:

           pdf_out1.addPage(pdf.getPage(i))

       else:

           pdf_out2.addPage(pdf.getPage(i))

   pdf_out1.write(f_out1)

   pdf_out2.write(f_out2)

# 再把后半个文件与前半个文件合并,后半个文件在前

with open(out_path, 'wb') as f_out:

   cnt_f, cnt_b = pdf_out1.getNumPages(), pdf_out2.getNumPages()

   pdf_out = PdfFileWriter()

   for i in range(cnt_b):

       pdf_out.addPage(pdf_out2.getPage(i))

   for i in range(cnt_f):

       pdf_out.addPage(pdf_out1.getPage(i))

   pdf_out.write(f_out)


PDF的旋转及排序


import pathlib

from PyPDF2 import PdfFileReader, PdfFileWriter

path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf')

f_path = path.joinpath('2020-新冠肺炎疫情对中国连锁餐饮行业的影响调研报告-中国连锁经营协会.pdf')

out_path = path.joinpath('002pdf_rotate.pdf')

with open(f_path, 'rb') as f, open(out_path, 'wb') as f_out:

   pdf = PdfFileReader(f)

   pdf_out = PdfFileWriter()

   page = pdf.getPage(0).rotateClockwise(90)

   pdf_out.addPage(page)

   # 把第二页放到前面

   pdf_out.addPage(pdf.getPage(2))

   page = pdf.getPage(1).rotateCounterClockwise(90)

   pdf_out.addPage(page)

   pdf_out.write(f_out)


PDF加水印及去水印


加图片水印,其实就是在页面中增加一个透明背景的图片,通过页面的mergePage方法即可完成。


import pathlib

from PyPDF2 import PdfFileReader, PdfFileWriter

path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf')

f_path = path.joinpath('2020-新冠肺炎疫情对中国连锁餐饮行业的影响调研报告-中国连锁经营协会.pdf')

wm_path = path.joinpath('watermark.pdf')

en_path = path.joinpath('002pdf_with_watermark_en.pdf')

out_path = path.joinpath('002pdf_with_watermark.pdf')

with open(f_path, 'rb') as f, open(wm_path, 'rb') as f_wm, open(out_path, 'wb') as f_out:

   pdf = PdfFileReader(f)

   pdf_wm = PdfFileReader(f_wm)

   pdf_out = PdfFileWriter()

   wm_cn_page = pdf_wm.getPage(0)

   wm_en_page = pdf_wm.getPage(1)

   cnt_pages = pdf.getNumPages()

   for i in range(cnt_pages):

       page = pdf.getPage(i)

       page.mergePage(wm_cn_page)

       pdf_out.addPage(page)

   pdf_out.write(f_out)


去水印,就比较复杂,需要根据不同情况具体分析。因为水印可能是文字、图片或者各种组合,关键是识别出特征。

去水印的3个常见思路参考:

1.找到特征词后替换,适合英文文档,但不适用于中文等CJK字符。

2.把PDF页转成图片后,用图像算法去水印,但这样会破坏文件原信息结构。

3.根据水印大小位置特征,找到所有元素后删除。这是更推荐的方式。

第3种方式效果最好,但如果碰到一些复杂的文档水印,就非常考验耐心。


你得一个个识别操作命令,一边替换一边检查效果,直到水印成功去除。

但,未必剩下的所有页都可以用同样特征模式来消除,因为这份PDF可能经过多人加水印,已经包含多种加水印方式。

所以,去水印并没有一种100%安全有效(不错删信息)且通用的方法。


加水印、去水印本质上是一种攻防策略。

比如一些工具推出去水印功能,一旦公开,加水印方就能识别并避开它的去除方法。


最后,尊重版权,是每个人应有的态度。

除了学习外,正式使用时,应该遵守内容创作方的规则。


PDF加密解密


PDF里的密码,分为用户密码和所有者密码。

PyPDF2里提供了基本的加密功能,“防君子不防小人”。

如果打开PDF文件后,复制了新文件,那新文件就不受所有者密码的约束,可被修改。


import pathlib

from PyPDF2 import PdfFileReader, PdfFileWriter

path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/002pdf')

f_path = path.joinpath('2020-新冠肺炎疫情对中国连锁餐饮行业的影响调研报告-中国连锁经营协会.pdf')

out_path_encrypt = path.joinpath('002pdf_encrypt.pdf')

out_path_decrypt = path.joinpath('002pdf_decrypt.pdf')

with open(f_path, 'rb') as f, open(out_path_encrypt, 'wb') as f_out:

   pdf = PdfFileReader(f)

   pdf_out = PdfFileWriter()

   cnt_pages = pdf.getNumPages()

   for i in range(cnt_pages):

       page = pdf.getPage(i)

       pdf_out.addPage(page)

   pdf_out.encrypt('123456', owner_pwd='654321')

   pdf_out.write(f_out)

# 重新读取加密文件并生成解密文件

with open(out_path_encrypt, 'rb') as f, open(out_path_decrypt, 'wb') as f_out:

   pdf = PdfFileReader(f)

   if not pdf.isEncrypted:

       print('文件未被加密')

   else:

       success = pdf.decrypt('123456')

       # if not success:

       pdf_out = PdfFileWriter()

       pdf_out.appendPagesFromReader(pdf)

       pdf_out.write(f_out)


因为篇幅有限,今天只讲解PyPDF2模块。下篇我们继续分享pdfminer.six和pdfplumber模块,大家不要错过~更多Python学习,就在云海天Python教程网

以上是 进阶PDF,就用Python(PyPDF2模块) 的全部内容, 来源链接: utcz.com/z/528959.html

回到顶部