合並pdf文件帶書簽代碼(利用python的PyPDF2,並解決PyPDF2 編碼問題'latin-1'和PyPDF2報錯:PdfReadError: EOF marker not found)


1.文件准備:

先將掃描的pdf文件,每一章放到一個文件夾中,文件夾名字用章節名命名。

這樣最終程序就能將章節名作為書簽了,而不是默認將每頁都生成書簽。

【最新代碼,更新PyPDF2后可用】用的3.8的python 2023.1.25更新

# -*- coding: utf-8 -*-
''' 本腳本用來合並pdf文件,支持帶一級子目錄的 每章內容分別放在不同的目錄下,目錄名為章節名 最終生成的pdf,按章節名生成書簽 '''

import os import sys import codecs from PyPDF2 import PdfReader, PdfFileWriter, PdfMerger import glob def getFileName(filepath): ''' 獲取當前目錄下的所有pdf文件 ''' file_list = glob.glob(filepath + "/*.pdf") # 默認安裝字典序排序,也可以安裝自定義的方式排序
    # file_list.sort()
    return file_list def get_dirs(filepath='', dirlist_out=[], dirpathlist_out=[]): # 遍歷filepath下的所有目錄
    for dir in os.listdir(filepath): dirpathlist_out.append(filepath + '\\' + dir) return dirpathlist_out def merge_childdir_files(path): ''' 每個子目錄下合並生成一個pdf ''' dirpathlist = get_dirs(path) if len(dirpathlist) == 0: print("當前目錄不存在子目錄") sys.exit() for dir in dirpathlist: mergefiles(dir, dir) def mergefiles(path, output_filename, import_outline=False): # 遍歷目錄下的所有pdf將其合並輸出到一個pdf文件中,輸出的pdf文件默認帶書簽,書簽名為之前的文件名
    # 默認情況下原始文件的書簽不會導入,使用import_outline=True可以將原文件所帶的書簽也導入到輸出的pdf文件中
    merger = PdfMerger() filelist = getFileName(path) if len(filelist) == 0: print("當前目錄及子目錄下不存在pdf文件") sys.exit() for filename in filelist: f = codecs.open(filename, 'rb') file_rd = PdfReader(f) short_filename = os.path.basename(os.path.splitext(filename)[0]) if file_rd.is_encrypted == True: print('不支持的加密文件:%s' % (filename)) continue merger.append(file_rd, outline_item=short_filename, import_outline=import_outline) print('合並文件:%s' % (filename)) f.close() # out_filename = os.path.join(os.path.abspath(path), output_filename)
    merger.write(output_filename + ".pdf") print('合並后的輸出文件:%s' % (output_filename)) merger.close() if __name__ == "__main__": # 每個章節一個子目錄,先分別合並每個子目錄文件為一個pdf,然后再將這些pdf合並為一個大的pdf,這樣做目的是想生成每個章節的書簽

    # 1.指定目錄
    # 原始pdf所在目錄(自行修改)
    path = "D:\信號與系統"
    # 輸出pdf路徑和文件名(自行修改)
    output_filename = "D:\信號與系統"

    # 2.生成子目錄的pdf
    # merge_childdir_files(path)

    # 3.子目錄pdf合並為總的pdf
    mergefiles(path, output_filename)

 

 2.程序代碼

代碼運行環境:python310

根據文件和文件夾所在位置改下面熒光黃的代碼部分即可

# -*- coding: utf-8 -*-''' 本腳本用來合並pdf文件,支持帶一級子目錄的 每章內容分別放在不同的目錄下,目錄名為章節名 最終生成的pdf,按章節名生成書簽 '''
import os import sys import codecs from PyPDF2 import PdfFileReader, PdfFileWriter, PdfFileMerger import glob def getFileName(filepath): ''' 獲取當前目錄下的所有pdf文件 ''' file_list = glob.glob(filepath + "/*.pdf") # 默認安裝字典序排序,也可以安裝自定義的方式排序
    # file_list.sort()
    return file_list def get_dirs(filepath='', dirlist_out=[], dirpathlist_out=[]): # 遍歷filepath下的所有目錄
    for dir in os.listdir(filepath): dirpathlist_out.append(filepath + '\\' + dir) return dirpathlist_out def merge_childdir_files(path): ''' 每個子目錄下合並生成一個pdf ''' dirpathlist = get_dirs(path) if len(dirpathlist) == 0: print("當前目錄不存在子目錄") sys.exit() for dir in dirpathlist: mergefiles(dir, dir) def mergefiles(path, output_filename, import_bookmarks=False): # 遍歷目錄下的所有pdf將其合並輸出到一個pdf文件中,輸出的pdf文件默認帶書簽,書簽名為之前的文件名
    # 默認情況下原始文件的書簽不會導入,使用import_bookmarks=True可以將原文件所帶的書簽也導入到輸出的pdf文件中
    merger = PdfFileMerger() filelist = getFileName(path) if len(filelist) == 0: print("當前目錄及子目錄下不存在pdf文件") sys.exit() for filename in filelist: f = codecs.open(filename, 'rb') file_rd = PdfFileReader(f) short_filename = os.path.basename(os.path.splitext(filename)[0]) if file_rd.isEncrypted == True: print('不支持的加密文件:%s' % (filename)) continue merger.append(file_rd, bookmark=short_filename, import_bookmarks=import_bookmarks) print('合並文件:%s' % (filename)) f.close() # out_filename = os.path.join(os.path.abspath(path), output_filename)
    merger.write(output_filename + ".pdf") print('合並后的輸出文件:%s' % (output_filename)) merger.close() if __name__ == "__main__": # 每個章節一個子目錄,先分別合並每個子目錄文件為一個pdf,然后再將這些pdf合並為一個大的pdf,這樣做目的是想生成每個章節的書簽

    # 1.指定目錄
    # 原始pdf所在目錄
    path = "D:\VScode\python-automate\pdf\第 01 章 電路模型與基本定律"
    # 輸出pdf路徑和文件名
    output_filename = "D:\VScode\python-automate\pdf\第 01 章 電路模型與基本定律"

    # 2.生成子目錄的pdf
    # merge_childdir_files(path)

    # 3.子目錄pdf合並為總的pdf
 mergefiles(path, output_filename) 

 


3.進一步合成
 

此時目標路徑會出現上述文件,若代碼運行成功后打開pdf無異常則表示第一章pdf已經合並好
且有每個文件名的標簽,接下來只需要對每個章節再次進行合並即可(很可惜我把所有章節合並后subtitle消失了)

合並完第一章合並完全部

 

 4.可能出現的問題
①PyPDF2 編碼問題'latin-1' codec can't encode characters in position 8-11: ordinal not in range(256)
通常這情況是出現了中文字符編碼
解決方法
1、修改pypdf2包中的generic.py文件
我的目錄是c:\……python310\Lib\site-packages\PyPDF2\generic.py
1)generic.py文件第488行原文
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: try: return NameObject(name.decode('gbk')) 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")

以及
2)修改pypdf2包中的utils.py文件
utils.py238行原文

r = s.encode('latin-1') if len(s) < 2: bc[s] = r return r

改成

try: r = s.encode('latin-1') if len(s) < 2: bc[s] = r return r except Exception as e: print(s) r = s.encode('utf-8') if len(s) < 2: bc[s] = r return r

②PyPDF2報錯:PdfReadError: EOF marker not found
查到的解決方法有
1)把文件復制到別的文件夾
2)打開文件再重新保存
3)(我自己)打開pdf發現有些pdf打開失敗於是重新合成

參考:
————————————————
版權聲明:本文為CSDN博主「huahuazhu」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:使用python合並pdf文件帶書簽_huahuazhu的博客-CSDN博客_python 合並pdf
版權聲明:本文為CSDN博主「小羊瓜瓜的博客」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM