Python中使用pypdf2合并、分割、加密pdf文件的代码详解
朋友需要对一个pdf文件进行分割,在网上查了查发现这个pypdf2可以完成这些操作,所以就研究了下这个库,并做一些记录。首先pypdf2是python3版本的,在之前的2版本有一个对应pypdf库。
可以使用pip直接安装:
pipinstallpypdf2
官方文档: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':'LaTeXwithhyperrefpackage','/CreationDate':"D:20150310202949-07'00'",'/PTEX.Fullbanner':'ThisispdfTeX,Version3.14159265-2.6-1.40.15(TeXLive2014/MacPorts2014_6)kpathseaversion6.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])或者一个PageRange来设定将fileobj中的指定范围的页面进行添加。
- merge(position,fileobj,bookmark=None,pages=None,import_bookmarks=True):与append方法类似,不过可以使用position参数指定添加的位置。
- write(fileobj):将数据写入到文件中。
使用的时候可以创建一个PdfFileMerger实例,然后使用append或者merge将想要融合的pdf文件依次添加进去,最后使用write保存即可。
defmerge_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)–Determineswhetherusershouldbewarnedofallproblemsandalsocausessomecorrectableproblemstobefatal.DefaultstoTrue.
确定是否应该警告用户所有问题,并且还会导致一些可纠正的问题。
刚开始感觉这个参数就是用来是否警告用户一些错误的,直接使用默认即可,但是当本人尝试合并带中文的pdf时,出现了如下错误:
Traceback(mostrecentcalllast):
File"I:\python3.5\lib\site-packages\PyPDF2\generic.py",line484,inreadFromStream
returnNameObject(name.decode('utf-8'))
UnicodeDecodeError:'utf-8'codeccan'tdecodebyte0xc8inposition10:invalidcontinuationbyte
Duringhandlingoftheaboveexception,anotherexceptionoccurred:
PyPDF2.utils.PdfReadError:IllegalcharacterinNameObject
在源码包中使用utf解码的时候出错了,尝试修改此处源码,让其使用gbk,但是还出现了其他的错误。最后发现当把构造函数中的strict设置为False时,控制台会打印下面的错误:
PdfReadWarning:IllegalcharacterinNameObject[generic.py:489]
但是两个文件成功的合并了,并且大概看了下合并后的文件有时好又是坏,同样的代码运行多次,有时候能够正常处理中文,但有时候中文乱码。
除了列出的方法还有一些其他的方法,比如添加书签、添加链接等等,可以参考官方文档。
对pdf进行合并、分割、加密。
整合出来了加密、解密、合并、根据页数进行分割、根据份数进行分割的样例:
使用注意:如果时中文文件,运行结果可能会出现乱码,但是多运行几次,中间有正常显示中文的问题。具体原因还不清楚,但就是这么玄学。。。
代码传送门
#@Time:2018/3/2623:48
#@Author:Leafage
#@File:handlePDF.py
#@Software:PyCharm
#@Describe:对pdf文件执行合并、分割、加密操作。
fromPyPDF2importPdfFileReader,PdfFileMerger,PdfFileWriter
defget_reader(filename,password):
try:
old_file=open(filename,'rb')
exceptIOErroraserr:
print('文件打开失败!'+str(err))
returnNone
#创建读实例
pdf_reader=PdfFileReader(old_file,strict=False)
#解密操作
ifpdf_reader.isEncrypted:
ifpasswordisNone:
print('%s文件被加密,需要密码!'%filename)
returnNone
else:
ifpdf_reader.decrypt(password)!=1:
print('%s密码不正确!'%filename)
returnNone
ifold_fileinlocals():
old_file.close()
returnpdf_reader
defencrypt_pdf(filename,new_password,old_password=None,encrypted_filename=None):
"""
对filename所对应的文件进行加密,并生成一个新的文件
:paramfilename:文件对应的路径
:paramnew_password:对文件加密使用的密码
:paramold_password:如果旧文件进行了加密,需要密码
:paramencrypted_filename:加密之后的文件名,省却时使用filename_encrypted;
:return:
"""
#创建一个Reader实例
pdf_reader=get_reader(filename,old_password)
ifpdf_readerisNone:
return
#创建一个写操作的实例
pdf_writer=PdfFileWriter()
#从之前Reader中将数据写入到Writer中
pdf_writer.appendPagesFromReader(pdf_reader)
#重新使用新密码加密
pdf_writer.encrypt(new_password)
ifencrypted_filenameisNone:
#使用旧文件名+encrypted作为新的文件名
encrypted_filename="".join(filename.split('.')[:-1])+'_'+'encrypted'+'.pdf'
pdf_writer.write(open(encrypted_filename,'wb'))
defdecrypt_pdf(filename,password,decrypted_filename=None):
"""
将加密的文件及逆行解密,并生成一个无需密码pdf文件
:paramfilename:原先加密的pdf文件
:parampassword:对应的密码
:paramdecrypted_filename:解密之后的文件名
:return:
"""
#生成一个Reader和Writer
pdf_reader=get_reader(filename,password)
ifpdf_readerisNone:
return
ifnotpdf_reader.isEncrypted:
print('文件没有被加密,无需操作!')
return
pdf_writer=PdfFileWriter()
pdf_writer.appendPagesFromReader(pdf_reader)
ifdecrypted_filenameisNone:
decrypted_filename="".join(filename.split('.')[:-1])+'_'+'decrypted'+'.pdf'
#写入新文件
pdf_writer.write(open(decrypted_filename,'wb'))
defsplit_by_pages(filename,pages,password=None):
"""
将文件按照页数进行平均分割
:paramfilename:所要分割的文件名
:parampages:分割之后每个文件对应的页数
:parampassword:如果文件加密,需要进行解密操作
:return:
"""
#得到Reader
pdf_reader=get_reader(filename,password)
ifpdf_readerisNone:
return
#得到总的页数
pages_nums=pdf_reader.numPages
ifpages<=1:
print('每份文件必须大于1页!')
return
#得到切分之后每个pdf文件的页数
pdf_num=pages_nums//pages+1ifpages_nums%pageselseint(pages_nums/pages)
print('pdf文件被分为%d份,每份有%d页!'%(pdf_num,pages))
#依次生成pdf文件
forcur_pdf_numinrange(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_numifcur_pdf_num!=pdf_numelsepages_nums
#print(str(start)+','+str(end))
#依次读取对应的页数
foriinrange(start,end):
pdf_writer.addPage(pdf_reader.getPage(i))
#写入文件
pdf_writer.write(open(split_pdf_name,'wb'))
defsplit_by_num(filename,nums,password=None):
"""
将pdf文件分为nums份
:paramfilename:文件名
:paramnums:要分成的份数
:parampassword:如果需要解密,输入密码
:return:
"""
pdf_reader=get_reader(filename,password)
ifnotpdf_reader:
return
ifnums<2:
print('份数不能小于2!')
return
#得到pdf的总页数
pages=pdf_reader.numPages
ifpagespdf1_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文件的代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!