当前位置: 首页 > news >正文

批量截取pdf文件

任务

现在我们有大量的pdf文件,我们想要截取每个文件中感兴趣的一部分,比如,我们下载了3500份上市公司的年度报告,我们想要找到包含“关键审计事项”部分内容,将pdf相关页保存为新的pdf文件。
python环境:

anaconda3
pdfminer3k
pypdf2

解析pdf文件

PDFMiner

PDFMiner是一个从PDF文档中提取信息的工具。与其他PDF相关的工具不同,它只用于获取和分析文本数据。PDFMiner能获取页面中文本的准确位置,以及字体或行等其他信息。它还有一个PDF转换器,可以将PDF文件转换成其他文本格式(如HTML)。还有一个可扩展的解析器PDF,可以用于文本分析以外的其他用途。

  1. 安装pdfminer3k
pip install pdfminder3k
  1. 解析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检索文本和元数据,还可以将整个文件合并在一起。

  1. 安装pypdf2:
pip install pypdf2
  1. 修改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
  1. 截取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

相关文章:

  • 怎么把视频里的音乐提取出来
  • 【BZOJ3097】 Hash Killer I
  • 从微服务迁移到工作流的经验之谈
  • ElasticSearch之Windows下安装
  • springboot学习之授权Spring Security
  • RSA
  • bzoj 3622 已经没有什么好害怕的了——二项式反演
  • Kendo DropDownListFor值传不回去的小坑
  • Java消息队列三道面试题详解!
  • 高性能两级缓存J2Cache
  • Webpack 4 学习01(基础配置)
  • 我的zsh配置, 2019最新方案
  • Java基础篇
  • 数据库基础SQL知识面试题二
  • 取代Python多进程!伯克利开源分布式框架Ray
  • create-react-app项目添加less配置
  • express + mock 让前后台并行开发
  • Gradle 5.0 正式版发布
  • JavaScript 一些 DOM 的知识点
  • Laravel5.4 Queues队列学习
  • python3 使用 asyncio 代替线程
  • Redis在Web项目中的应用与实践
  • tweak 支持第三方库
  • Zepto.js源码学习之二
  • 对象管理器(defineProperty)学习笔记
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 实习面试笔记
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • puppet连载22:define用法
  • RDS-Mysql 物理备份恢复到本地数据库上
  • ​Python 3 新特性:类型注解
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (day 12)JavaScript学习笔记(数组3)
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (六)vue-router+UI组件库
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .net core 依赖注入的基本用发
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .net MVC中使用angularJs刷新页面数据列表
  • .Net8 Blazor 尝鲜
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • @hook扩展分析
  • @selector(..)警告提示
  • [04]Web前端进阶—JS伪数组
  • [20150707]外部表与rowid.txt
  • [2021]Zookeeper getAcl命令未授权访问漏洞概述与解决
  • [Android] Implementation vs API dependency
  • [Android]一个简单使用Handler做Timer的例子
  • [AutoSar]BSW_Com02 PDU详解
  • [AutoSar]BSW_Com07 CAN报文接收流程的函数调用
  • [BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)