Python实现按序合并多个pdf文件

技术交流QQ群:1027579432,欢迎你的加入!

欢迎关注我的微信公众号:CurryCoder的程序人生

1.整体实现步骤

  • 在日常办公中,我们可能会有一个需求,需要将多个pdf文件合并成一个文件。例如:需要将每个章节的pdf文件学习资料合并成一个pdf文件,便于我们进行学习资料的归档与整理。如何才能合并多个pdf文件呢?我查了一下网上现有的资料(详见参考资料1),发现python中有一个第三方库PyPDF2用起来真香。但是,网上现有的程序仅仅实现多个pdf文件,并未实现按序合并。下面谈谈我的整体实现步骤:
    • (1).既然有轮子了,那就直接开干吧。win+R键打开cmd,执行pip install PyPDF2安装PyPDF2第三方库。由于我之前已经安装该第三方库,所以实际安装结果以你电脑安装结果为准。
    • (2).解决PyPDF2第三方库产生的编码问题,此解决方法来源于参考资料2。
Traceback (most recent call last):
  File "D:\python35\Lib\site-packages\PyPDF2\generic.py", line 484, in readFromStream
    return NameObject(name.decode('utf-8'))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcb in position 8: invalid continuation byte
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "D:\python35\Lib\apps\backstage\views\busi_contract_manage_view.py", line 703, in post
    merge_pdf_result = merge_pdf(final_files, pdf_path)
  File "D:\python35\Lib\apps\utils\doc_convert_util.py", line 86, in merge_pdf
    pdf_writer.write(new_file)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 482, in write
    self._sweepIndirectReferences(externalReferenceMap, self._root)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 571, in _sweepIndirectReferences
    self._sweepIndirectReferences(externMap, realdata)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 547, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 571, in _sweepIndirectReferences
    self._sweepIndirectReferences(externMap, realdata)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 547, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 556, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, data[i])
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 571, in _sweepIndirectReferences
    self._sweepIndirectReferences(externMap, realdata)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 547, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 547, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 547, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 577, in _sweepIndirectReferences
    newobj = data.pdf.getObject(data)
  File "D:\python35\Lib\site-packages\PyPDF2\pdf.py", line 1611, in getObject
    retval = readObject(self.stream, self)
  File "D:\python35\Lib\site-packages\PyPDF2\generic.py", line 66, in readObject
    return DictionaryObject.readFromStream(stream, pdf)
  File "D:\python35\Lib\site-packages\PyPDF2\generic.py", line 579, in readFromStream
    value = readObject(stream, pdf)
  File "D:\python35\Lib\site-packages\PyPDF2\generic.py", line 60, in readObject
    return NameObject.readFromStream(stream, pdf)
  File "D:\python35\Lib\site-packages\PyPDF2\generic.py", line 492, in readFromStream
    raise utils.PdfReadError("Illegal character in Name Object")
PyPDF2.utils.PdfReadError: Illegal character in Name Object
  • 分析上面报错部分,可以看出错误来源于D:\python35\Lib\site-packages\PyPDF2\generic.py", line 484。generic.py文件第484行,原始内容为:
try:
    return NameObject(name.decode('utf-8'))
except (UnicodeEncodeError, UnicodeDecodeError) as e:
    # Name objects should represent irregular characters
    # with a '#' followed by the symbol's hex number
    if not pdf.strict:
        warnings.warn("Illegal character in Name Object", utils.PdfReadWarning)
        return NameObject(name)
    else:
        raise utils.PdfReadError("Illegal character in Name Object")
  • 需要将上述原始内容,修改为如下内容:
try:
            return NameObject(name.decode('utf-8'))
        except (UnicodeEncodeError, UnicodeDecodeError) as e:
            # Name objects should represent irregular characters
            # with a '#' followed by the symbol's hex number
            try:
                return NameObject(name.decode('gbk'))
            except (UnicodeEncodeError, UnicodeDecodeError) as e:
                if not pdf.strict:
                    warnings.warn("Illegal character in Name Object", utils.PdfReadWarning)
                    return NameObject(name)
                else:
                    raise utils.PdfReadError("Illegal character in Name Object")
  • 接着,修改utils.py文件中的第238行。utils.py文件中的第238行原始内容如下所示:
r = s.encode('latin-1')
if len(s) < 2:
    bc[s] = r
return r
  • 需要将上述原始内容,修改为如下内容:
try:
    r = s.encode('latin-1')
except Exception as e:
    r = s.encode('utf-8')
if len(s) < 2:
    bc[s] = r
return r
  • (3).对需要合并的多个pdf文件进行重命名,文件命名为1.pdf、2.pdf、3.pdf,依次类推。此处仍可以优化,欢迎各位大佬提出宝贵意见。
  • (4).将需要合并的多个pdf文件放置在同一个文件夹下,程序执行结束后输出合并后的文件名为merge.pdf(可以在程序中自定义该文件名)。

2.按序合并的整体思路[完整程序见最后]

  • (1).对所有待合并的pdf文件名以1、2、3等数字来进行重命名。进行对于实现多个pdf文件按序合并,我想了一些方法。例如:根据文件的创建时间进行排序,根据原始的文件名称进行排序。最终,想了一个很简单的方法。即对原始文件手动以数字来重命名,这样做是为了便于以文件名来排序。之前想直接以原始的文件名来重命名,但是考虑到中文名称的pdf文件与英文名称的pdf文件没法直接通过比较文件名来实现排序后合并,因此将所有待合并的pdf文件统一以数字来重命名。此处,应该有更好的方法,欢迎各位大佬提出宝贵意见。
  • (2).提取当前文件夹下以.pdf后缀结束的所有文件。既然所有待合并的pdf文件名都被重命名成了数字,那直接对这些数字排序,排序后直接合并就好啦。
  • (3).从文件完整名称中,提取出文件名(不包括文件的.pdf后缀部分)。由于我们利用的是文件名字来实现排序,用来排序的文件名字是不含文件后缀名.pdf的。所以,必须想办法(通过切片操作实现)将.pdf后缀给弄掉。
  • (4).对文件名字进行排序。注意一下:提取处理的文件名字是str类型的,需要转成int类型后再排序一下。
  • (5).利用字符串拼接对排序后的文件名字与.pdf后缀进行拼接,拼接成一个含有文件后缀名的文件名。此处应该有更好的方法,字符串拼接比较慢,占内存!有其他更好的方法,欢迎各位大佬提出。
  • (6).直接利用现有轮子PyPDF2库中的PdfFileMerger()类实例化出一个file_merger对象,调用已有API实现多个pdf文件的按序合并。注意:此时的文件已经是按照文件名排好序啦。
import os
from PyPDF2 import PdfFileMerger


target_path = 'E:\\Flask\\'

pdf_lst = [f for f in os.listdir(target_path) if f.endswith('.pdf')]


tmp = []
for item in pdf_lst:
    tmp.append(item[:-4])

res = [int(i) for i in tmp if isinstance(i, str)]

ss = sorted(res)
ll = []
for j in ss:
    sj = str(j) + ".pdf"
    ll.append(sj)

pdf_lst = [os.path.join(target_path, filename) for filename in ll]

file_merger = PdfFileMerger()
for pdf in pdf_lst:
    file_merger.append(pdf)     # 合并pdf文件

file_merger.write("E://Flask//merge.pdf")

3.参考资料

  • 1.PyPDF2库官方说明
  • 2.PyPDF2编码问题解决方法

你可能感兴趣的:(python3进阶)