Python自动化与PDF
1.相关准备
Python 操作 PDF 会用到两个库,分别是:PyPDF2 和 pdfplumber。其中 PyPDF2 可以更好的读取、写入、分割、合并PDF文件,而 pdfplumber 可以更好的读取 PDF 文件中内容和提取 PDF 中的表格。
win+r 后输入 cmd 打开 command 窗口,依次输入pip install PyPDF2
和pip install pdfplumber
进行Python库的安装。
2.批量拆分
导入相关的库
from PyPDF2 import PdfFileWriter, PdfFileReader
import os
def split_pdf(filename, filepath, save_dirpath, step=5):
"""
拆分PDF为多个小的PDF文件,
@param filename:文件名
@param filepath:文件路径
@param save_dirpath:保存小的PDF的文件路径
@param step: 每step间隔的页面生成一个文件,例如step=5,表示0-4页、5-9页...为一个文件
@return:
"""
if not os.path.exists(save_dirpath):
os.mkdir(save_dirpath)
pdf_reader = PdfFileReader(filepath)
# 读取每一页的数据
pages = pdf_reader.getNumPages()
for page in range(0, pages, step):
pdf_writer = PdfFileWriter()
# 拆分pdf,每 step 页的拆分为一个文件
for index in range(page, page+step):
if index < pages:
pdf_writer.addPage(pdf_reader.getPage(index))
# 保存拆分后的小文件
save_path = os.path.join(save_dirpath, filename+str(int(page/step)+1)+'.pdf')
print(save_path)
with open(save_path, "wb") as out:
pdf_writer.write(out)
print("文件已成功拆分,保存路径为:"+save_dirpath)
split_pdf("易方达中小盘混合型证券投资基金2020年中期报告", r'C:\Users\XP_Fan\Desktop\task\易方达中小盘混合型证券投资基金2020年中期报告.pdf', r'C:\Users\XP_Fan\Desktop\task\report')
值得注意的是,再第一次运行代码是会出现失败,并报如下的错误:
这个时候需要找到Python库PyPDF2的安装位置,并打开utils.py文件,定位到第238行的位置,将原文中的代码进行修改。
原文中的代码是下面这样的:
r = s.encode('latin-1')
if len(s) < 2:
bc[s] = r
return r
我们需要将它修改为:
try:
r = s.encode('latin-1')
if len(s) < 2:
bc[s] = r
return r
except Exception as e:
r = s.encode('utf-8')
if len(s) < 2:
bc[s] = r
return r
3.批量合并
def concat_pdf(filename, read_dirpath, save_filepath):
"""
合并多个PDF文件
@param filename:文件名
@param read_dirpath:要合并的PDF目录
@param save_filepath:合并后的PDF文件路径
@return:
"""
pdf_writer = PdfFileWriter()
# 对文件名进行排序
list_filename = os.listdir(read_dirpath)
list_filename.sort(key=lambda x: int(x[:-4].replace(filename, "")))
for filename in list_filename:
print(filename)
filepath = os.path.join(read_dirpath, filename)
# 读取文件并获取文件的页数
pdf_reader = PdfFileReader(filepath)
pages = pdf_reader.getNumPages()
# 逐页添加
for page in range(pages):
pdf_writer.addPage(pdf_reader.getPage(page))
# 保存合并后的文件
with open(save_filepath, "wb") as out:
pdf_writer.write(out)
print("文件已成功合并,保存路径为:"+save_filepath)
concat_pdf("易方达中小盘混合型证券投资基金2020年中期报告", r'C:\Users\XP_Fan\Desktop\task\report', r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-合并.pdf')
4.提取文字内容
import pdfplumber
def extract_text_info(filepath):
"""
提取PDF中的文字
@param filepath:文件路径
@return:
"""
with pdfplumber.open(filepath) as pdf:
# 获取第2页数据
page = pdf.pages[1]
print(page.extract_text())
extract_text_info(r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-合并.pdf')
上面我们提取的文字是pdf文件的第2页内容,也可以通过修改代码提取pdf文件中所有的文字。
def extract_text_info(filepath):
"""
提取PDF中的文字
@param filepath:文件路径
@return:
"""
with pdfplumber.open(filepath) as pdf:
# 获取全部数据
for page in pdf.pages:
print(page.extract_text())
extract_text_info(r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-合并.pdf')
5.提取表格内容
def extract_table_info(filepath):
"""
提取PDF中的图表数据
@param filepath:
@return:
"""
with pdfplumber.open(filepath) as pdf:
# 获取第18页数据
page = pdf.pages[24]
# 如果一页有一个表格,设置表格的第一行为表头,其余为数据
table_info = page.extract_table()
df_table = pd.DataFrame(table_info[1:], columns=table_info[0])
df_table.to_csv('dmeo.csv', index=False, encoding='gbk')
extract_table_info(r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-合并.pdf')
针对再同一页中存在多个表格的情况,可以通过修改代码进行提取。
#提取多个表格
def extract_table_info(filepath):
"""
提取PDF中的图表数据
@param filepath:
@return:
"""
with pdfplumber.open(filepath) as pdf:
# 获取第18页数据
page = pdf.pages[17]
# 如果一页有多个表格,对应的数据是一个三维数组
tables_info = page.extract_tables()
for index in range(len(tables_info)):
# 设置表格的第一行为表头,其余为数据
df_table = pd.DataFrame(tables_info[index][1:], columns=tables_info[index][0])
print(df_table)
#df_table.to_csv('dmeos.csv', index=False, encoding='gbk')
extract_table_info(r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-合并.pdf')
6.提取图片内容
import fitz
pic_dirpath = r'C:\Users\XP_Fan\Desktop\task\photo'
filepath = r'C:\Users\XP_Fan\Desktop\task\易方达中小盘混合型证券投资基金2020年中期报告.pdf'
if not os.path.exists(pic_dirpath):
os.makedirs(pic_dirpath)
# 使用正则表达式来查找图片
check_XObject = r"/Type(?= */XObject)"
check_Image = r"/Subtype(?= */Image)"
img_count = 0
"""1. 打开pdf,打印相关信息"""
pdf_info = fitz.open(filepath)
# 1.16.8版本用法 xref_len = doc._getXrefLength()
# 最新版本写法
xref_len = pdf_info.xref_length()
# 打印PDF的信息
print("文件名:{}, 页数: {}, 对象: {}".format(filepath, len(pdf_info), xref_len-1))
"""2. 遍历PDF中的对象,遇到是图像才进行下一步,不然就continue"""
for index in range(1, xref_len):
# 1.16.8版本用法 text = doc._getXrefString(index)
# 最新版本
text = pdf_info.xref_object(index)
is_XObject = re.search(check_XObject, text)
is_Image = re.search(check_Image, text)
# 如果不是对象也不是图片,则不操作
if is_XObject or is_Image:
img_count += 1
# 根据索引生成图像
pix = fitz.Pixmap(pdf_info, index)
pic_filepath = os.path.join(pic_dirpath, 'img_' + str(img_count) + '.png')
"""pix.size 可以反映像素多少,简单的色素块该值较低,可以通过设置一个阈值过滤。以阈值 10000 为例过滤"""
# if pix.size < 10000:
# continue
"""三、 将图像存为png格式"""
if pix.n >= 5:
# 先转换CMYK
pix = fitz.Pixmap(fitz.csRGB, pix)
# 存为PNG
pix.writePNG(pic_filepath)
7.转换为图片
将pdf文件的每一页转换为图片,需要安装相应的库和组件。
通过命令pip install pdf2image
安装pdf2image库,关于pdf2image库的更多信息可以通过它的GitHub地址:https://github.com/Belval/pdf2image进行更深入的学习。
针对windows系统,需要安装poppler for Windows组件,安装链接:http://blog.alivate.com.au/poppler-windows/,将组件下载好之后,需要添加环境变量,将bin文件夹的路径添加到环境变量PATH中,并重启电脑使环境变量配置完成。
对于 mac 用户,需要安装 poppler for Mac,具体可以参考这个链接:http://macappstore.org/poppler/
from pdf2image import convert_from_path,convert_from_bytes
filepath = r'C:/Users/XP_Fan/Desktop/task/易方达中小盘混合型证券投资基金2020年中期报告.pdf'
pic_dirpath = r'C:/Users/XP_Fan/Desktop/task/png/'
if not os.path.exists(pic_dirpath):
os.makedirs(pic_dirpath)
images = convert_from_bytes(open(filepath, 'rb').read())
# images = convert_from_path(filepath, dpi=200)
for image in images:
# 保存图片
pic_filepath = os.path.join(pic_dirpath, 'img_'+str(images.index(image))+'.png')
image.save(pic_filepath, 'PNG')
8.添加水印
PDF 中添加水印,首先需要一个水印PDF文件,然后依次通过 mergePage 操作将每一页的 PDF 文件合并到水印文件上,从而得到一个带有水印的 PDF 文件。
watermark_filepath =r'C:/Users/XP_Fan/Desktop/task/watermark.png'
filepath =r'C:/Users/XP_Fan/Desktop/task/易方达中小盘混合型证券投资基金2020年中期报告.pdf'
save_filepath =r'C:/Users/XP_Fan/Desktop/task/易方达中小盘混合型证券投资基金2020年中期报告-水印.pdf'
watermark = PdfFileReader(watermark_filepath)
watermark_page = watermark.getPage(0)
pdf_reader = PdfFileReader(filepath)
pdf_writer = PdfFileWriter()
for page_index in range(pdf_reader.getNumPages()):
current_page = pdf_reader.getPage(page_index)
# 封面页不添加水印
if page_index == 0:
new_page = current_page
else:
new_page = copy(watermark_page)
new_page.mergePage(current_page)
pdf_writer.addPage(new_page)
# 保存水印后的文件
with open(save_filepath, "wb") as out:
pdf_writer.write(out)
9.文档加密和解密
filepath = r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-合并.pdf'
save_filepath = r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-加密.pdf'
passwd = 'jiami123'
pdf_reader = PdfFileReader(filepath)
pdf_writer = PdfFileWriter()
for page_index in range(pdf_reader.getNumPages()):
pdf_writer.addPage(pdf_reader.getPage(page_index))
# 添加密码
pdf_writer.encrypt(passwd)
with open(save_filepath, "wb") as out:
pdf_writer.write(out)
通过运行上面的代码之后,我们找到再对应位置生成的新的文件打开会发现该文件已经被加密了,需要输入口令才能够打开。
同样的,我们也可以通过Python对其进行解密
filepath = r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-加密.pdf'
save_filepath = r'C:\Users\XP_Fan\Desktop\易方达中小盘混合型证券投资基金2020年中期报告-解密.pdf'
passwd = 'jiami123'
pdf_reader = PdfFileReader(filepath)
# PDF文档解密
pdf_reader.decrypt(passwd)
pdf_writer = PdfFileWriter()
for page_index in range(pdf_reader.getNumPages()):
pdf_writer.addPage(pdf_reader.getPage(page_index))
with open(save_filepath, "wb") as out:
pdf_writer.write(out)