侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Python中使用pypdf2合并、分割、加密pdf文件的代码详解

Python  /  管理员 发布于 7年前   448

朋友需要对一个pdf文件进行分割,在网上查了查发现这个pypdf2可以完成这些操作,所以就研究了下这个库,并做一些记录。首先pypdf2是python3版本的,在之前的2版本有一个对应pypdf库。

可以使用pip直接安装:

pip install pypdf2

官方文档: pythonhosted.org/PyPDF2/

里面主要有这几个类:

PdfFileReader 。

该类主要提供了对pdf文件的读操作,其构造方法为:

PdfFileReader(stream, strict=True, warndest=None, overwriteWarnings=True)

第一个参数可以传入一个文件流,或者一个文件路径。后面三个参数都是用来设置警告的处理方式,直接使用默认的即可。

得到实例之后,就可以对pdf进行一些操作了。主要的有以下几个操作:

  • decrypt(password):如果pdf文件加密的话,可以使用该方法对其解密。
  • getDocumentInfo():检索pdf文件的一些信息。其返回值为一个DocumentInformation 类型,直接输出的话会得到类似下面的信息:
{'/ModDate': "D:20150310202949-07'00'", '/Title': '', '/Creator': 'LaTeX with hyperref package', '/CreationDate': "D:20150310202949-07'00'", '/PTEX.Fullbanner': 'This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014/MacPorts 2014_6) kpathsea version 6.2.0', '/Producer': 'pdfTeX-1.40.15', '/Keywords': '', '/Trapped': '/False', '/Author': '', '/Subject': ''}

  • getNumPages():这个会pdf文件中的页数。
  • getPage(pageNumber):会得到pdf文件中对应的pageNumber页数的页面对象,返回值为PageObject实例。在得到PageObject实例之后就可以将其加添、插入等操作。
  • getPageNumber(page):与上面的方法对立,可以传入PageObject实例,然后得到该实例是pdf文件中第几页的。
  • getOutlines(node=None, outlines=None):检索文档中出现的文档大纲。
  • isEncrypted:记录该pdf是否加密。如果文件本身加密,即使在使用解密decrypt方法之后,还是会返回true。
  • numPages:pdf总共的页数,相当于访问getNumPages()的只读属性。

PdfFileWriter 。

该类支持对pdf文件进行写操作,通常是使用PdfFileReader读取一些pdf数据,然后使用该类进行一些操作。

创建该类的实例时不需要参数。

其主要的方法有:

  • addAttachment(fname, fdata):向pdf添加文件。
  • addBlankPage(width=None, height=None):给pdf添加一个空白页到最后,如果没有指定大小就使用当前Weiter中pdf最后一页的大小。
  • addPage(page):添加page到pdf中,通常这个page是由上面的Reader获取的。
  • appendPagesFromReader(reader, after_page_append=None):将reader中的数据拷贝到当前的Writer实例中,并且如果指定after_page_append的话,最后还有回掉该函数并且将writer中的数据传入其中。
  • encrypt(user_pwd, owner_pwd=None, use_128bit=True):将pdf进行加密,其中官方说userpwd是允许用户使用一些限制的权限打开pdf文件,也就是使用该密码的话可能会有一些限制,但是本人并没有在文档中找到设置权限的内容。而ownerpwd则是允许用户无限制的使用。第三个参数是是否使用128位加密。
  • getNumPages():得到pdf页数。
  • getPage(pageNumber):得到对应页数的Page,是一个PageObject对象,可以使用上面的addPage方法将page进行添加。
  • insertPage(page, index=0):将page添加到pdf中,index指定的是被插入的位置。
  • write(stream):将该Writer中的内容写入到文件中。

PdfFileMerger。

该类用来合并pdf文件,该类的构造方法有一个参数:PdfFileMerger(strict=True),注意这里的参数后面会介绍:

常用方法:

  • addBookmark(title, pagenum, parent=None):给pdf添加一个书签,title是书签的标题,pagenum是该书签指向的页面。
  • append(fileobj, bookmark=None, pages=None, import_bookmarks=True):将指定的fileobj文件添加到文件的末尾,bookmark是赎前,pages可以使用(start, stop[, step])或者一个 Page Range来设定将fileobj中的指定范围的页面进行添加。
  • merge(position, fileobj, bookmark=None, pages=None, import_bookmarks=True):与append方法类似,不过可以使用position参数指定添加的位置。
  • write(fileobj):将数据写入到文件中。

使用的时候可以创建一个PdfFileMerger实例,然后使用append或者merge将想要融合的pdf文件依次添加进去,最后使用write保存即可。

def merge_pdf():  # 创建一个用来合并文件的实例  pdf_merger = PdfFileMerger()  # 首先添加一个Week1_1.pdf文件  pdf_merger.append('Week1_1.pdf')  # 然后在第0页后面添加ex1.pdf文件  pdf_merger.merge(0, 'ex1.pdf')  # 添加书签  pdf_merger.addBookmark('这是一个书签', 1)  # 将其写入到文件中  pdf_merger.write('merge_pdf.pdf')

下面看一下PdfFileMerger(strict=True)中的这个参数:

官方对这个参数的解释:

strict (bool) C Determines whether user should be warned of all problems and also causes some correctable problems to be fatal. Defaults to True.

确定是否应该警告用户所有问题,并且还会导致一些可纠正的问题。

刚开始感觉这个参数就是用来是否警告用户一些错误的,直接使用默认即可,但是当本人尝试合并带中文的pdf时,出现了如下错误:

Traceback (most recent call last): File "I:\python3.5\lib\site-packages\PyPDF2\generic.py", line 484, in readFromStream  return NameObject(name.decode('utf-8'))UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc8 in position 10: invalid continuation byteDuring handling of the above exception, another exception occurred:PyPDF2.utils.PdfReadError: Illegal character in Name Object

在源码包中使用utf解码的时候出错了,尝试修改此处源码,让其使用gbk,但是还出现了其他的错误。最后发现当把构造函数中的strict设置为False时,控制台会打印下面的错误:

PdfReadWarning: Illegal character in Name Object [generic.py:489]

但是两个文件成功的合并了,并且大概看了下合并后的文件有时好又是坏,同样的代码运行多次,有时候能够正常处理中文,但有时候中文乱码。

除了列出的方法还有一些其他的方法,比如添加书签、添加链接等等,可以参考官方文档。

对pdf进行合并、分割、加密。

整合出来了加密、解密、合并、根据页数进行分割、根据份数进行分割的样例:

使用注意:如果时中文文件,运行结果可能会出现乱码,但是多运行几次,中间有正常显示中文的问题。具体原因还不清楚,但就是这么玄学。。。

代码传送门

# @Time  : 2018/3/26 23:48# @Author : Leafage# @File  : handlePDF.py# @Software: PyCharm# @Describe: 对pdf文件执行合并、分割、加密操作。from PyPDF2 import PdfFileReader, PdfFileMerger, PdfFileWriterdef get_reader(filename, password):  try:    old_file = open(filename, 'rb')  except IOError as err:    print('文件打开失败!' + str(err))    return None  # 创建读实例  pdf_reader = PdfFileReader(old_file, strict=False)  # 解密操作  if pdf_reader.isEncrypted:    if password is None:      print('%s文件被加密,需要密码!' % filename)      return None    else:      if pdf_reader.decrypt(password) != 1:        print('%s密码不正确!' % filename)        return None  if old_file in locals():    old_file.close()  return pdf_readerdef encrypt_pdf(filename, new_password, old_password=None, encrypted_filename=None):  """  对filename所对应的文件进行加密,并生成一个新的文件  :param filename: 文件对应的路径  :param new_password: 对文件加密使用的密码  :param old_password: 如果旧文件进行了加密,需要密码  :param encrypted_filename: 加密之后的文件名,省却时使用filename_encrypted;  :return:  """  # 创建一个Reader实例  pdf_reader = get_reader(filename, old_password)  if pdf_reader is None:    return  # 创建一个写操作的实例  pdf_writer = PdfFileWriter()  # 从之前Reader中将数据写入到Writer中  pdf_writer.appendPagesFromReader(pdf_reader)  # 重新使用新密码加密  pdf_writer.encrypt(new_password)  if encrypted_filename is None:    # 使用旧文件名 + encrypted 作为新的文件名    encrypted_filename = "".join(filename.split('.')[:-1]) + '_' + 'encrypted' + '.pdf'  pdf_writer.write(open(encrypted_filename, 'wb'))def decrypt_pdf(filename, password, decrypted_filename=None):  """  将加密的文件及逆行解密,并生成一个无需密码pdf文件  :param filename: 原先加密的pdf文件  :param password: 对应的密码  :param decrypted_filename: 解密之后的文件名  :return:  """  # 生成一个Reader和Writer  pdf_reader = get_reader(filename, password)  if pdf_reader is None:    return  if not pdf_reader.isEncrypted:    print('文件没有被加密,无需操作!')    return  pdf_writer = PdfFileWriter()  pdf_writer.appendPagesFromReader(pdf_reader)  if decrypted_filename is None:    decrypted_filename = "".join(filename.split('.')[:-1]) + '_' + 'decrypted' + '.pdf'  # 写入新文件  pdf_writer.write(open(decrypted_filename, 'wb'))def split_by_pages(filename, pages, password=None):  """  将文件按照页数进行平均分割  :param filename: 所要分割的文件名  :param pages: 分割之后每个文件对应的页数  :param password: 如果文件加密,需要进行解密操作  :return:  """  # 得到Reader  pdf_reader = get_reader(filename, password)  if pdf_reader is None:    return  # 得到总的页数  pages_nums = pdf_reader.numPages  if pages <= 1:    print('每份文件必须大于1页!')    return  # 得到切分之后每个pdf文件的页数  pdf_num = pages_nums // pages + 1 if pages_nums % pages else int(pages_nums / pages)  print('pdf文件被分为%d份,每份有%d页!' % (pdf_num, pages))  # 依次生成pdf文件  for cur_pdf_num in range(1, pdf_num + 1):    # 创建一个新的写实例    pdf_writer = PdfFileWriter()    # 生成对应的文件名称    split_pdf_name = "".join(filename)[:-1] + '_' + str(cur_pdf_num) + '.pdf'    # 计算出当前开始的位置    start = pages * (cur_pdf_num - 1)    # 计算出结束的位置,如果是最后一份就直接返回最后的页数,否则用每份页数*已经分好的文件数    end = pages * cur_pdf_num if cur_pdf_num != pdf_num else pages_nums    # print(str(start) + ',' + str(end))    # 依次读取对应的页数    for i in range(start, end):      pdf_writer.addPage(pdf_reader.getPage(i))    # 写入文件    pdf_writer.write(open(split_pdf_name, 'wb'))def split_by_num(filename, nums, password=None):  """  将pdf文件分为nums份  :param filename: 文件名  :param nums: 要分成的份数  :param password: 如果需要解密,输入密码  :return:  """  pdf_reader = get_reader(filename, password)  if not pdf_reader:    return  if nums < 2:    print('份数不能小于2!')    return  # 得到pdf的总页数  pages = pdf_reader.numPages  if pages < nums:    print('份数不应该大于pdf总页数!')    return  # 计算每份应该有多少页  each_pdf = pages // nums  print('pdf共有%d页,分为%d份,每份有%d页!' % (pages, nums, each_pdf))  for num in range(1, nums + 1):    pdf_writer = PdfFileWriter()    # 生成对应的文件名称    split_pdf_name = "".join(filename)[:-1] + '_' + str(num) + '.pdf'    # 计算出当前开始的位置    start = each_pdf * (num - 1)    # 计算出结束的位置,如果是最后一份就直接返回最后的页数,否则用每份页数*已经分好的文件数    end = each_pdf * num if num != nums else pages    print(str(start) + ',' + str(end))    for i in range(start, end):      pdf_writer.addPage(pdf_reader.getPage(i))    pdf_writer.write(open(split_pdf_name, 'wb'))def merger_pdf(filenames, merged_name, passwords=None):  """  传进来一个文件列表,将其依次融合起来  :param filenames: 文件列表  :param passwords: 对应的密码列表  :return:  """  # 计算共有多少文件  filenums = len(filenames)  # 注意需要使用False 参数  pdf_merger = PdfFileMerger(False)  for i in range(filenums):    # 得到密码    if passwords is None:      password = None    else:      password = passwords[i]    pdf_reader = get_reader(filenames[i], password)    if not pdf_reader:      return    # append默认添加到最后    pdf_merger.append(pdf_reader)  pdf_merger.write(open(merged_name, 'wb'))def insert_pdf(pdf1, pdf2, insert_num, merged_name, password1=None, password2=None):  """  将pdf2全部文件插入到pdf1中第insert_num页  :param pdf1: pdf1文件名称  :param pdf2: pdf2文件名称  :param insert_num: 插入的页数  :param merged_name: 融合后的文件名称  :param password1: pdf1对应的密码  :param password2: pdf2对应的密码  :return:  """  pdf1_reader = get_reader(pdf1, password1)  pdf2_reader = get_reader(pdf2, password2)  # 如果有一个打不开就返回  if not pdf1_reader or not pdf2_reader:    return  # 得到pdf1的总页数  pdf1_pages = pdf1_reader.numPages  if insert_num < 0 or insert_num > pdf1_pages:    print('插入位置异常,想要插入的页数为:%d,pdf1文件共有:%d页!' % (insert_num, pdf1_pages))    return  # 注意需要使用False参数,可能会出现中文乱码的情况  m_pdf = PdfFileMerger(False)  m_pdf.append(pdf1)  m_pdf.merge(insert_num, pdf2)  m_pdf.write(open(merged_name, 'wb'))if __name__ == '__main__':  # encrypt_pdf('ex1.pdf', 'leafage')  # decrypt_pdf('ex1123_encrypted.pdf', 'leafage')  # split_by_pages('ex1.pdf', 5)  split_by_num('ex2.pdf', 3)  # merger_pdf(['ex1.pdf', 'ex2.pdf'], 'merger.pdf')  # insert_pdf('ex1.pdf', 'ex2.pdf', 10, 'pdf12.pdf')

总结

以上所述是小编给大家介绍的Python中使用pypdf2合并、分割、加密pdf文件的代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!


  • 上一条:
    python多进程读图提取特征存npy
    下一条:
    python+selenium实现简历自动刷新的示例代码
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客