任务
现在我们有大量的pdf文件,我们想要截取每个文件中感兴趣的一部分,比如,我们下载了3500份上市公司的年度报告,我们想要找到包含“关键审计事项”部分内容,将pdf相关页保存为新的pdf文件。
python环境:
anaconda3
pdfminer3k
pypdf2
解析pdf文件
PDFMiner
PDFMiner是一个从PDF文档中提取信息的工具。与其他PDF相关的工具不同,它只用于获取和分析文本数据。PDFMiner能获取页面中文本的准确位置,以及字体或行等其他信息。它还有一个PDF转换器,可以将PDF文件转换成其他文本格式(如HTML)。还有一个可扩展的解析器PDF,可以用于文本分析以外的其他用途。
- 安装pdfminer3k
pip install pdfminder3k
- 解析pdf,匹配关键字,返回其所在页码
from pdfminer.pdfparser import PDFParser, PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LTTextBoxHorizontal, LAParams
from pdfminer.pdfinterp import PDFTextExtractionNotAllowed
path = r'./report/603999读者传媒2017年年度报告.pdf'
def parse(path):
"""
#解析pdf文件,并将文字内容更保存在文本中
返回“关键字”所在的页码
"""
fp = open(path, 'rb') # 以二进制读模式打开
# 用文件对象来创建一个pdf文档分析器
praser = PDFParser(fp)
# 创建一个PDF文档
doc = PDFDocument()
# 连接分析器 与文档对象
praser.set_document(doc)
doc.set_parser(praser)
# 提供初始化密码
# 如果没有密码 就创建一个空的字符串
doc.initialize()
# 检测文档是否提供txt转换,不提供就忽略
if not doc.is_extractable:
raise PDFTextExtractionNotAllowed
else:
# 创建PDf 资源管理器 来管理共享资源
rsrcmgr = PDFResourceManager()
# 创建一个PDF设备对象
laparams = LAParams()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
# 创建一个PDF解释器对象
interpreter = PDFPageInterpreter(rsrcmgr, device)
# 循环遍历列表,每次处理一个page的内容
page_num=0
key_flag=False
for page in doc.get_pages(): # doc.get_pages() 获取page列表
if key_flag: #如果找到第一个关键字,则退出解析
break
page_num=page_num+1
interpreter.process_page(page)
# 接受该页面的LTPage对象
layout = device.get_result()
# 这里layout是一个LTPage对象 里面存放着 这个page解析出的各种对象 一般包括LTTextBox, LTFigure, LTImage, LTTextBoxHorizontal 等等 想要获取文本就获得对象的text属性,
for x in layout:
if (isinstance(x, LTTextBoxHorizontal)):
results = x.get_text()
if "关键审计事项" in results: # 匹配到关键字,则退出该页的循环
key_flag=True
break
return page_num
裁剪pdf文件
PyPDF2
PyPDF2是一个python PDF库,能够分割、合并、裁剪和转换PDF文件的页面。它还可以向PDF文件中添加自定义数据、查看选项和密码。它可以从PDF检索文本和元数据,还可以将整个文件合并在一起。
- 安装pypdf2:
pip install pypdf2
- 修改pypdf2的源码
利用pypdf2截取pdf中的某几页,如果pdf的中文字编码为ANSI编码,则无法解析。对于pypdf2对于gbk不支持的现象,需要对以下两处进行修改。参考
tips:
ANSI是一种字符代码,为使计算机支持更多语言。ANSI编码表示英文字符时用一个字节,表示中文用两个或四个字节。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码。
GBK是在国家标准GB2312基础上扩容后兼容GB2312的标准。
其一:
在文件*Miniconda3libsite-packagesPyPDF2generic.py"中第488行,改为
此处是为了适应含有‘gbk’的编码的中文字符,提供对其的解码能力。
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
ret=name.decode('gbk')
return NameObject(ret)
其二:
在文件*Miniconda3libsite-packagesPyPDF2utils.py中,第238-241行,改为:
此处是为了适应‘utf-8’的编码情况;
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
- 截取pdf特定页
from PyPDF2 import PdfFileWriter, PdfFileReader
def pdfCrap(path,start_page,save_path):
"""
从pdf文件中截取几页,并保存在对应pdf文件中
"""
# 开始页
start_page = start_page - 1
# 截止页
end_page = start_page + 5 # 这里设定截取5页
output = PdfFileWriter()
pdf_file = PdfFileReader(open(path, "rb"), strict=False)
pdf_pages_len = pdf_file.getNumPages()
for i in range(start_page, end_page):
output.addPage(pdf_file.getPage(i)) # 在输出流中添加页
outputStream = open(save_path, "wb")
output.write(outputStream)
遍历文件夹内所有文件
以上已经可以实现对单一pdf文件的解析以及提取特定页了,剩下的就是将整个流程串联起来,实现批量pdf文件的截取。
import os
def file_name(file_dir):
"""
获取某文件夹下,特定扩展名的文件名,
返回特定扩展名文件列表
"""
L=[]
for root, dirs, files in os.walk(file_dir):
for file in files:
if os.path.splitext(file)[1] == '.pdf': #os.path.splitext()函数将路径拆分为文件名+扩展名
L.append(file)
return L
出错怎么办
现在,我们已经能够进行批量pdf文件的裁剪了,但在实际操作中,会发生许多意外,使得程序被中断,为了避免中断影响批处理的进程,现在添加异常处理功能。
file_path = './report/' # 输入文件所在的文件夹
result_path = './result/' # 输出文件所在文件夹
def getAll():
"""
批量裁剪pdf文件,添加程序log,记录文件名,关键字起始页码,并且对异常情况进行记录;
"""
files=file_name(file_path)
for file in files:
try:
path = file_path + file
page=parse(path)
save_path=result_path+file
pdfCrap(path,page,save_path)
if page==0: #如果识别出起始页码为0,说明关键字未被找到,需要记录。
raise Exception("page_num=0")
with open(r'./log.txt', 'a', encoding='utf-8') as f:
f.write(file+' start_page: '+str(page)+"\n")
except Exception as e: # 捕获错误
print('error: ', e)
with open(r'./error_log.txt', 'a', encoding='utf-8') as f:
f.write(file+' start_page: '+str(page)+"\n")
continue
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。