大家好,我早起。在上一期《Python办公自动化》案例中,我们介绍了如何有选择地提取某些页面进行合并。但在很多情况下,我们并不是预测要提取的页码,而是想将包含指定内容的页面提取并合并成一个新的PDF。本文以两个真实需求为例进行讲解。01、需求描述数据为某上市公司公开年报286页PDF,大致如下:现在需要用Python完成以下两个需求:需求一:提取所有包含单词的页面策略合并新的PDF需求2:提取所有包含图片的页面分别保存为PDF文件。02.前置知识及逻辑梳理2.1PyPDF2模块实现合并PyPDF2导入模块的代码往往是:fromPyPDF2importPdfFileReader,PdfFileWriter这里导入了两个方法:PdfFileReader可以理解为readerPdfFileWriter可以理解为writer的如下逻辑使用PyPDF2实现合并操作:读者阅读一次所有的pdf,读者将阅读的内容传递给作者,作者统一输出到一个新的pdf隐含知识点:读者只能通过以下方式交出阅读内容页面pagetothewriter2.2获取和添加页面这两个代码在我们之前的推文中提到过,下面列为回顾:.getPage获取特定页面。addPage添加特定页面2.3图片和文字处理要实现本文的需求,需要做一个很重要的判断:判断页面中是否包含文字或图片。判断是否包含特定文本相对简单。在遍历每个页面时,将其中包含的文本提取出来,在字符串级别进行判断。代码思路:使用pdfplumber打开PDF文件获取指定页面,或者遍历每个页面使用.extract_text()方法提取当前页面的文本判断“策略”是否判断是否包含图片在提取的文本中,思路和上面类似,只是方法不同。考虑使用正则方法识别图片,使用fitz和re配合。有关详细信息,请参见下面的代码03。代码实现3.1需求1的实现首先完成需求1的任务,导入需要的库:读写PDF文件的PyPDF2并提取文本pdfplumberfromPyPDF2importPdfFileReader,PdfFileWriteimportpdfplumber指定文件所在路径,初始化writer同时,将文件交给读者:path=r'C:\xxxxxx'pdf_writer=PdfFileWriter()pdf_reader=PdfFileReader(path+r'\companyannualreport.PDF')通过pdfplumber打开文件contextmanager的形式,使用.getNumPages获取reader的最大页数遍历每一页提取文本:withpdfplumber.open(path+r'\companyannualreport.PDF')aspdf:foriinrange(pdf_reader.getNumPages()):page=pdf.pages[i]print(page.extract_text())我们提取文本的目的是判断符合要求的页码作为阅读器.getPage的参数,最后使用.addPage给作者:withpdfplumber。打开(路径+r'\公司年报.PDF')aspdf:foriinrange(pdf_reader.getNumPages()):page=pdf.pages[i]print(page.extract_text())if'strategy'inpage.extract_text():pdf_writer.addPage(pdf_reader.getPage(i))print(i+1,page.extract_text())识别完成后让writer按需输出文件名:withopen(path+r'\new_companyannualreport.pdf','wb')asout:pdf_writer.write(out)至此,我们完成了对包含特定文本内容的页面的提取,并整合为一个PDF。所有页面均包含“攻略”二字:需求1的完整代码如下,有兴趣的读者可以自行研究。fromPyPDF2importPdfFileReader,PdfFileWriteimportpdfplumberpath=r'C:\xxx'pdf_writer=PdfFileWriter()pdf_reader=PdfFileReader(path+r'\公司年报.PDF')withpdfplumber.open(path+r'\公司年报.PDF')aspdf:foriinrange(pdf_reader.getNumPages()):page=pdf.pages[i]print(page.extract_text())if'strategy'inpage.extract_text():pdf_writer.addPage(pdf_reader.getPage(i))print(i+1、page.extract_text())withopen(path+r'\new_companyannualreport1.pdf','wb')asout:pdf_writer.write(out)3.2Requirement2的实现接下来,完成Requirement2的任务。首先导入需要的库:fromPyPDF2importPdfFileReader,PdfFileWriteimportfitzimportreimportos指定文件所在路径:path=r'C:\xxxxxx'正则识别图片部分就不详述了,之前的推文已经介绍过了,看代码直接:page_lst=[]checkImg=r"/Subtype(?=*/Image)"pdf=fitz.open(path+r'\companyannualreport.PDF')lenXREF=pdf._getXrefLength()foriinrange(lenXREF):text=pdf._getXrefString(i)isImage=re.search(checkImg,text)ifisImage:page_lst.append(i)print(page_lst)得到所有包含图片的页面后,结合读者的配合和作者一样,可以完成新PDF的生成。注意这个要求是把所有图片单独输出,所以拿到页面后,交给writer直接输出成文件:pdf_reader=PdfFileReader(path+r'\companyannualreport.PDF')forpageinpage_lst:pdf_writer=PdfFileWriter()pdf_writer.addPage(pdf_reader.getPage(page))withopen(path+r'\公司年报_{}.pdf'.format(page+1),'wb')asout:pdf_writer.write(out)至此,第二个需求已经完成。需要注意的是,目前还没有完善的提取PDF图片的方法,本案例介绍的方法在识别图片方面并不稳定。读者可以用自己的数据进行实验。完整代码如下:fromPyPDF2importPdfFileReader,PdfFileWriterimportfitzimportreimportospath=r'C:\xxx'page_lst=[]checkImg=r"/Subtype(?=*/Image)"pdf=fitz.open(path+r'\companyannualreport.PDF')lenXREF=pdf._getXrefLength()foriinrange(lenXREF):text=pdf._getXrefString(i)isImage=re.search(checkImg,text)ifisImage:page_lst.append(i)print(page_lst)pdf_reader=PdfFileReader(path+r'\公司年报.PDF')forpageinpage_lst:pdf_writer=PdfFileWriter()pdf_writer.addPage(pdf_reader.getPage(page))withopen(path+r'\公司年报_{}.pdf'.format(page+1),'wb')asout:pdf_writer.write(out)这两个单体需求实现后,可以将相关代码封装起来,结合os等模块实现批量操作,解放双手。
