利用tampermonkey的知網下載助手腳本下載pdf格式論文時,發現論文缺少書簽,而腳本可以下載一個txt格式的書簽(目錄),因此打算利用python將txt格式的目錄添加到pdf中。
txt文件解析
txt 文件的讀取
利用python讀取txt文件時,使用的是python中的open方法,讀取文件時最好加上文件的編碼方式。不然有可能出現以下錯誤:
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa6 in position 14: illegal multibyte sequence
這是因為再windows系統中python讀取文件時的默認編碼方式不是utf-8導致的。
實現代碼如下:
txtpath = "D:/目錄.txt"
with open(txtpath,'r',encoding='utf-8') as f:
list_data = f.read()
print(type(list_data)) # 打印文件讀取得到變量的類型,輸出結果為“str”,即文本類型
print(list_data)
轉義字符的原樣打印
通過以上步驟已經可以讀取到txt文件中的內容了,由於換行符、制表符在使用print輸出時會可視化輸出,而不是輸出\t \n 這種轉移字符,而在進行文本內容解析時需要對這些轉義字符進行解析,所以現在需要將轉義字符原樣輸出,其方法是將字符串放到數組中,然后打印數組。
實現代碼如下:
str='致謝\t5\n摘要\t6\nABSTRACT\t8\n1 緒論\t16\n\t1.1 研究意義\t16\n\t'
print([str])
輸出結果:
['致謝\t5\n摘要\t6\nABSTRACT\t8\n1 緒論\t16\n\t1.1 研究意義\t16\n\t']
txt文件按行讀取
而通過分析txt中的文本可以發現,txt文件中的內容每行對應一條目錄,因此可以采用按行讀取的方式一行行的去對文本進行解析。
按行讀取的代碼如下:
with open(txtpath,'r',encoding='utf-8') as f:
for line in f:
print([line]) # 將行文本放在數組中打印是為了查看轉移字符
pass
書簽文本信息解析
觀察每行文本信息可以發現,書簽的格式大致為
\t書簽文字\t頁碼\n
因此解析文本時,可以先找到倒數第一個\t制表符,從而確定頁碼,然后就可以將頁碼以外的文本全部放到書簽文本中了,而書簽是幾級書簽,可以通過每行文字前面\t制表符的個數確定。
參考代碼:
for line in f:
line=line.replace('\n','') #去除\n
i0=line.rfind('\t') #在find前加r的效果是從后往前搜索
page_num=int(line[i0+1:])
line=line[:i0]
i0=0
page_grade=0
while(line.find('\t',i0,-1)!=-1):
page_grade+=1
i0 = line.find('\t',i0+1,-1) #更新i0,繼續搜索
page_text=line.replace('\t','')
需要注意的是,在查找倒數第一個\t的時候,使用的事rfind函數,而不是find函數,這兩個函數的區別是,rfind函數從后往前找,find函數從前往后找。
為pdf文件添加書簽
這里需要用到的是PyPDF2庫,大概流程是用reader讀取pdf,將reader讀取到的pdf復制到writer中,然后給writer中的pdf添加標簽,最后保存pdf即可。
實現的代碼如下:
from PyPDF2 import PdfFileReader as reader,PdfFileWriter as writer
pdfpath = "D:/1.pdf"
pdf_in = reader(pdfpath)
pdf_out = writer()
# 將讀取的pdf放到writer中
pageCount = pdf_in.getNumPages()
for iPage in range(pageCount):
pdf_out.addPage(pdf_in.getPage(iPage))
parent0=pdf_out.addBookmark('父目錄',0,parent = None) # 添加父目錄
# 使用方法: addBookmark(書簽文字,書簽頁碼,書簽的父目錄),返回值是書簽(可以作為其他書簽的父目錄)
parent1=pdf_out.addBookmark('子目錄',0,parent = parent0) # 給父添加子目錄
# 保存pdf
with open('D:/1-bookmark.pdf','wb') as fout:
pdf_out.write(fout)
在復制pdf時沒有使用cloneDocumentFromReader()方法,因為實際使用時發現使用了這個方法會在添加書簽時報錯,所以使用了一個折衷的方式,單頁pdf復制。
ValueError: {'/Type': '/Outlines'} is not in list
為了讓書簽正確的添加到其父目錄底下,程序在設計時引用了一個parent列表,實現方法如下:
parent=[]
curren_grade=0
parent.append(None)
with open(txtpath,'r',encoding='utf-8') as f:
for line in f:
line=line.replace('\n','') #去除\n
i0=line.rfind('\t') #在find前加r的效果是從后往前搜索
page_num=int(line[i0+1:])
line=line[:i0]
i0=0
page_grade=0
while(line.find('\t',i0,-1)!=-1):
page_grade+=1
i0 = line.find('\t',i0+1,-1) #更新i0,繼續搜索
page_text=line.replace('\t','')
# 動態調整parent列表的長度
if curren_grade<page_grade:
parent.append(None)
curren_grade=page_grade
if page_grade==0:
parent[0]=pdf_out.addBookmark(page_text,page_num-1,parent = None)
else:
parent[page_grade]=pdf_out.addBookmark(page_text,page_num-1,parent = parent[page_grade-1])
同時引用了一個curren_grade變量,讓程序中的parent列表長度可以根據目錄總的等級數量動態的調整。
完整代碼
以下是程序的完整代碼,程序中txtpath為書簽文件的輸入路徑,pdfpath為pdf路徑,添加書簽后的pdf輸出路徑為源目錄下pdf文件文件名后添加_bm。
比如輸入pdf路徑為D:/1.pdf時,輸出的路徑就是D:/1_bm.pdf
import numpy as np
from PyPDF2 import PdfFileReader as reader,PdfFileWriter as writer
import os
def add_bookmarks(txtpath,pdfpath):
pdf_in = reader(pdfpath)
pdf_out = writer()
# pdf_out.cloneDocumentFromReader(pdf_in,after_page_append=None)
parent=[]
pageCount = pdf_in.getNumPages()
for iPage in range(pageCount):
pdf_out.addPage(pdf_in.getPage(iPage))
curren_grade=0
parent.append(None)
with open(txtpath,'r',encoding='utf-8') as f:
for line in f:
line=line.replace('\n','') #去除\n
i0=line.rfind('\t') #在find前加r的效果是從后往前搜索
page_num=int(line[i0+1:])
line=line[:i0]
i0=0
page_grade=0
while(line.find('\t',i0,-1)!=-1):
page_grade+=1
i0 = line.find('\t',i0+1,-1) #更新i0,繼續搜索
if curren_grade<page_grade:
parent.append(None)
curren_grade=page_grade
page_text=line.replace('\t','')
if page_grade==0:
parent[0]=pdf_out.addBookmark(page_text,page_num-1,parent = None)
else:
parent[page_grade]=pdf_out.addBookmark(page_text,page_num-1,parent = parent[page_grade-1])
outpath = pdfpath[:-4]+'_bm.pdf'
with open(outpath,'wb') as fout:
pdf_out.write(fout)
if __name__ == '__main__':
txtpath = "D:/1_目錄.txt"
pdfpath = "D:/1.pdf"
add_bookmarks(txtpath,pdfpath)
優化程序
接下來讓程序自動的讀取當前python腳本文件路徑下的pdf文件和txt書簽,然后匹配書簽后再自動添加書簽,避免手動輸入路徑的繁瑣。
(未完待續)
參考
用Python為PDF文件批量添加書簽 - 簡書
用python合並pdf,並添加書簽_BlowfishKing的博客-CSDN博客
python 反向查找_weixin_30596735的博客-CSDN博客
【python】讀取和輸出到txt_flora-CSDN博客
python遍歷目錄下所有文件 - 努力奮斗小青年 - 博客園
