实用脚本!Python 提取 PDF 指定内容生成新文件!-程序员宅基地

技术标签: python  java  数据分析  机器学习  人工智能  

很多时候,我们并不会预知希望提取的页号,而是希望将包含指定内容的页面提取合并为新PDF,本文就以两个真实需求为例进行讲解。

01

需求描述

数据是一份有286页的上市公司公开年报PDF,大致如下06092587da3b61eec5c08061f87656d6.png

现在需要利用 Python 完成以下两个需求

需求一:提取所有包含 战略 二字的页面并合并新PDF

需求二:提取所有包含图片的页面,并分别保存为 PDF 文件

02

前置知识和逻辑梳理

2.1 PyPDF2 模块实现合并

PyPDF2 导入模块的代码常常是:

from PyPDF2 import PdfFileReader, PdfFileWriter

这里导入了两个方法:

  1. PdfFileReader 可以理解为读取器

  2. PdfFileWriter 可以理解为写入器

利用 PyPDF2 实现合并运用的一下逻辑:

  1. 读取器将所有pdf读取一遍

  2. 读取器将读取的内容交给写入器

  3. 写入器统一输出到一个新pdf

隐含知识点:读取器只能将读取的内容一页一页交给写入器

2.2 获取与添加页面

之前我们的推文中提到这两个代码,下面列出作为复习:

  1. .getPage 获取特定页

  2. .addPage 添加特定页

2.3 图片和文字的处理

要实现本文的需求还要做到很重要的一个判断:确定页面中有无包含的文字或图片

判断是否包含特定的文字比较简单,遍历每一页的时候都将包含的文本抽提出,做字符串层面的判断即可,代码思路:

  1. 利用 pdfplumber 打开PDF 文件

  2. 获取指定的页,或者遍历每一页

  3. 利用 .extract_text() 方法提取当前页的文字

  4. 判断 “战略” 是否在提取的文字中

判断是否包含图片,思路和上面是类似的,但方法不同。图片考虑用正则的方法识别,用 fitzre 配合,具体见下文代码

03

代码实现

3.1 需求一的实现

首先来完成需求一的任务,导入需要用到的库:读取写入PDF文件的 PyPDF2 以及抽提文本的 pdfplumber

from PyPDF2 import PdfFileReader, PdfFileWriter
import pdfplumber

指定文件所在的路径,同时初始化写入器,将文件交给读取器:

path = r'C:\xxxxxx'
pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(path + r'\公司年报.PDF')

以上下文管理器形式通过 pdfplumber 打开文件,同时用 .getNumPages 获取读取器的最大页利于遍历每一页来抽提文字:

with pdfplumber.open(path + r'\公司年报.PDF') as pdf:
    for i in range(pdf_reader.getNumPages()):
        page = pdf.pages[i]
        print(page.extract_text())

我们抽提文字的目的是用来判断,将符合要求的页码作为读取器 .getPage 的参数,最后用 .addPage 交给写入器:

with pdfplumber.open(path + r'\公司年报.PDF') as pdf:
    for i in range(pdf_reader.getNumPages()):
        page = pdf.pages[i]
        print(page.extract_text())
        if '战略' in page.extract_text():
            pdf_writer.addPage(pdf_reader.getPage(i))
            print(i + 1, page.extract_text())

完成识别后让写入器输出为需要的文件名:

with open(path + r'\new_公司年报.pdf', 'wb') as out:
    pdf_writer.write(out)

至此,我们就完成了包含特定文字内容页面的提取,并整合成一个PDF。所有的页面均包含“战略”二字:

9abff99c55c4a7aa94843430124edf5e.png91abe6d5d91a0cfa67b2eee525aaab7e.png

需求一完整代码如下,感兴趣的读者可以自行研究

from PyPDF2 import PdfFileReader, PdfFileWriter
import pdfplumber

path = r'C:\xxx'
pdf_writer = PdfFileWriter()
pdf_reader = PdfFileReader(path + r'\公司年报.PDF')

with pdfplumber.open(path + r'\公司年报.PDF') as pdf:
    for i in range(pdf_reader.getNumPages()):
        page = pdf.pages[i]
        print(page.extract_text())
        if '战略' in page.extract_text():
            pdf_writer.addPage(pdf_reader.getPage(i))
            print(i + 1, page.extract_text())

with open(path + r'\new_公司年报1.pdf', 'wb') as out:
    pdf_writer.write(out)

3.2 需求二的实现

接下来完成需求二的任务。首先导入需要的库:

from PyPDF2 import PdfFileReader, PdfFileWriter
import fitz
import re
import os

指定文件所在的路径:

path = r'C:\xxxxxx'

正则识别图片的部分不细讲,之前的推文已经介绍过,我们直接看代码:

page_lst = []
checkImg = r"/Subtype(?= */Image)"
pdf = fitz.open(path + r'\公司年报.PDF')
lenXREF = pdf._getXrefLength()

for i in range(lenXREF):
    text = pdf._getXrefString(i)
    isImage = re.search(checkImg, text)
    if isImage:
        page_lst.append(i)

print(page_lst)
9f551bcabf84da0eb20aa72d55168349.png

获取到所有包含图片的页面后,再结合读取器和写入器的配合就能完成新 PDF 的产生。注意本需求是所有图片单独输出,因此获取到页面后交给写入器直接输出成文件:

pdf_reader = PdfFileReader(path + r'\公司年报.PDF')
for page in page_lst:
    pdf_writer = PdfFileWriter()
    pdf_writer.addPage(pdf_reader.getPage(page))
    with open(path + r'\公司年报_{}.pdf'.format(page + 1), 'wb') as out:
        pdf_writer.write(out)

至此也完成了第二个需求。需要说明的是目前没有非常完美提取PDF图片的方法,本案例介绍的方法识别图片也并不稳定。读者可以利用自己的数据多做尝试。完整代码如下:

from PyPDF2 import PdfFileReader, PdfFileWriter
import fitz
import re
import os

path = r'C:\xxx'

page_lst = []
checkImg = r"/Subtype(?= */Image)"
pdf = fitz.open(path + r'\公司年报.PDF')
lenXREF = pdf._getXrefLength()
for i in range(lenXREF):
    text = pdf._getXrefString(i)
    isImage = re.search(checkImg, text)
    if isImage:
        page_lst.append(i)

print(page_lst)

pdf_reader = PdfFileReader(path + r'\公司年报.PDF')
for page in page_lst:
    pdf_writer = PdfFileWriter()
    pdf_writer.addPage(pdf_reader.getPage(page))
    with open(path + r'\公司年报_{}.pdf'.format(page + 1), 'wb') as out:
        pdf_writer.write(out)

实现这两个单个需求后,就可以将相关代码封装并结合os等模块实现批量操作,解放双手。

END

推荐阅读



牛逼!Python常用数据类型的基本操作(长文系列第①篇)
牛逼!Python的判断、循环和各种表达式(长文系列第②篇)

牛逼!Python函数和文件操作(长文系列第③篇)

牛逼!Python错误、异常和模块(长文系列第④篇)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_40787712/article/details/124010562

智能推荐

程序员如何优化自己的职业程序?_程序员怎么走系统优化路线-程序员宅基地

文章浏览阅读700次。突如其来的疫情,打乱了所有职场人的节奏。年前裸辞,打算年后再找工作,拿到offer的希望,瞬间渺茫;领了年终奖,准备迎接“跳槽季”,迎来的是裁员、企业倒闭。困守在家中的人,也在线上交流着对未来的担忧:“疫情什么时候结束?我该怎么安排跳槽节奏?”“公司会不会裁员?我该怎么让自己不可替代?”“疫情对行业有什么影响?我要不要朝线上发展?”这些..._程序员怎么走系统优化路线

GCN公式中特征矩阵和邻接矩阵的处理_(1)获取点簇特征矩阵x,并计算邻接矩阵a,自连接邻接矩阵 , 及 ; (2)输入两层的gcn-程序员宅基地

文章浏览阅读1.2w次,点赞7次,收藏45次。https://mp.weixin.qq.com/s?__biz=MzI0ODcxODk5OA==&mid=2247509797&idx=4&sn=0f356b8f6397ad6e0743e192ed182ede&chksm=e99e94dcdee91dcad9bc842ed129b27e7cfe649fc3407e21e7e0c05060c7f6daf346d628f189&mpshare=1&scene=23&srcid=0617GtNIERqCM_(1)获取点簇特征矩阵x,并计算邻接矩阵a,自连接邻接矩阵 , 及 ; (2)输入两层的gcn

上传文件前先预览图片的实现方法_php 上传前 预览图片-程序员宅基地

文章浏览阅读625次。https://blog.csdn.net/fireofjava/article/details/46011381_php 上传前 预览图片

java操作redis简单示例_java redis hashmap-程序员宅基地

文章浏览阅读4k次。java操作redis简单示例_java redis hashmap

python 输出颜色与样式的方法_python windll getstdhandle-程序员宅基地

文章浏览阅读2.9k次。上次遇到这个问题就想写下来,其实当时我也不怎么会,老师说这个东西不需要理解,只需要死记硬背,写的多了就记住了,所以今天搜集了几篇文章,加上自己的理解,写下了这篇python 输出颜色的样式与方法的文章,一方面想自己记录下自己的理解,另一方面想用自己通俗的理解送给需要的盆友。在写python 程序代码的时候,我们知道python 输出的字符串颜色和一般字符相同,但是许多时候,我们需要强调某些字符..._python windll getstdhandle

中国移动一键登录 —— Flutter(安卓)_flutter一键登录-程序员宅基地

文章浏览阅读3.7k次,点赞4次,收藏15次。随着Flutter逐渐的被越来越多的公司所采用,那么如何将中国移动统一认证SDK接入到Flutter应用中。目录1.准备工作2.开始接入3.运行调试4.注意事项1.准备工作1.1 下载好统一认证SDK,这里使用的版本是quick_login_android_5.8.11.2 在移动开发平台申请好应用拿到appid、appkey2.开始接入2.1 按照统一认证SDK接入文档配置好权限,READ_PHONE_STATE权限根据应用方需要选择是否添加,SDK不强制要求._flutter一键登录

随便推点

python字符串案例_python中str1 = 'abcdefg' str2 = 'abcdefg' print(id(-程序员宅基地

文章浏览阅读870次。str1 = 'abcdefg higklmn'print(str1)print(id(str1))print(type(str1))print(dir(str1))print(str1[1:2])# 修改字符串,拼接字符串print(str1 + ' HELLO WORD')# 格式化字符串print('姓名 %s 年龄 %d' % ('小白',10))..._python中str1 = 'abcdefg' str2 = 'abcdefg' print(id(str1)) print(id(str2))

飞思卡尔S12系列(基于MC9S12XET256MAA和/MC9S12XEP100)中断PIT资料总结和分享_pitmtld0=100-1;设置定时器0的计数值,这里设置为99,用于生成一个定时周期。pitld-程序员宅基地

文章浏览阅读2.9k次。核心内容就在这张图上:   相信大家都明白总线的概念,在图中可以看到6个定时器模块,Micro Timer 0、Micro Timer 1、Timer 0、Timer 1、Timer 2、Timer 3,其中前两个是8位的,后四个是16位的。从图中可以看出PIT模块是以总线时钟(Bus Clock)为基准时钟的,总线时钟通过8位Micro Timer 0和Micro Timer 1倍频..._pitmtld0=100-1;设置定时器0的计数值,这里设置为99,用于生成一个定时周期。pitld0=10000-1;设置定时器0的加载值,这里设置为9999,表示定时器0的溢出时间为10000个时钟周期。

ModBus-RTU通讯协议编程_modbusrtu设备编程-程序员宅基地

文章浏览阅读7.5k次,点赞9次,收藏78次。编者说:ModBus通信协议结构简单,编程方便,在工业应用现场被广泛使用,特别是PLC应用场合。需要指出的是,ModBus只是一种通信协议,即设备之间的数据约束方式,使用时需要有底层的驱动程序支持,例如,串口通讯。串口通信使用简单,在ModBus协议中应用广泛。在信号的传输方式上又分为RS-232通信,RS-485通信,这种区分只是在数据的传输方式方作划分,底层的驱动程序完全一样。需要长距离、..._modbusrtu设备编程

DayDayUP_linuxC学习日记 _利用C实现文件的复制_该程序通过从待复制的文件中逐步读出数据到缓冲区,再把缓冲区的数据逐个写入-程序员宅基地

文章浏览阅读607次。read/write 的使用 读函数read ssize_t read(int fd,void *buf,size_t nbyte)read函数是负责从fd中读取内容.成功时,read返回实际所读的字节数,如果返回的值是0,表示已经读到文件的结束了. 小于0表示出现了错误.如果错误为EINTR说明读是由中断引起的, 如果是ECONNREST表示网络连接出了问题. 写函数write ssi_该程序通过从待复制的文件中逐步读出数据到缓冲区,再把缓冲区的数据逐个写入

maven项目简介-程序员宅基地

文章浏览阅读1.1k次。前言: 最近在公司接触的项目都是"maven项目",可是除了安装了一个maven的插件和项目中多了一个maven的配置文件外,对于maven的事情就看不到了,于是今天查了一些关于maven的相关介绍,跟大家一个分享下,看看maven到底是个什么东东... 正题:1.Maven能做什么? Maven是一个强大的构建工具,可以帮我们自动化构建过程,从清理、编译、测试_maven项目

Windows高级调试 调试工具简介_windbg logviewer.exe-程序员宅基地

文章浏览阅读2.6k次。第一部分 概 述第1章 调试工具简介许多技术性的书籍和文章都指出了在正确的软件设计和软件工程原则中包含的重要性。有些书侧重于介绍在方法与实践之间的均衡性,而有些书则注重对方法的描述。一些书讨论了面向对象设计、设计模式以及模块化编程等方法,这些方法都能帮助我们编写出更强大的软件。毫无疑问,正确的软件开发方法是所有软件项目获得成功的必要条件。然而,它们却并不是软件项目_windbg logviewer.exe