热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于Python实现PDF区域文本提取工具_python

这篇文章主要为大家介绍了如何通过Python实现一个非常精简的图像化的PDF区域选择提取工具,文中示例代码讲解详细,感兴趣

功能简介

打开软件后界面如下:

点击打开文件按钮打开之前的PDF文件后效果如下:

框选区域后,标题栏会自动显示当前框选的区域提取到的文字,还可以左右按钮切换:

实际我们需要提取文字的区域可能不止这一个,所以程序支持多区域框选:

完成区域框选后就可以点击保存文件,将PDF每页提取到的文本保存到一个csv文件中,当前选区的保存结果如下:

可以看到已经按框选顺序依次保存了每一个区域的字符串。

如果选择区域时发现提取结果不准确,可以撤销后重新选择:

保存图片则会将PDF的每页的整体保存为一张图片,未选择区域时,以页码为文件名保存图片:

选择区域时,会自动提取最后一个区域提取的文本作为当前页的文件名:

开发代码

当然这个项目由于本人是一次使用wxpython,功能非常简约,现在将完整代码开源出来期待各位大佬的改进。

源码和已编译工具下载地址:

https://codechina.csdn.net/as604049322/python_gui

完整代码:

"""
小小明的代码
CSDN主页:https://blog.csdn.net/as604049322
"""
__author__ = '小小明'
__time__ = '2021/11/24'
import csv
import wx
import os
import fitz
class MyCanvas(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.parent = parent
self.rects = []
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonEvent)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftButtonEvent)
self.Bind(wx.EVT_MOTION, self.OnLeftButtonEvent)
self.Bind(wx.EVT_PAINT, self.DoDrawing)
b = wx.Button(self, -1, "打开文件", (0, 0))
self.Bind(wx.EVT_BUTTON, self.OnButton, b)
b = wx.Button(self, -1, "保存文件", (75, 0))
self.Bind(wx.EVT_BUTTON, self.save_file, b)
b = wx.Button(self, -1, "保存图片", (150, 0))
self.Bind(wx.EVT_BUTTON, self.save_img, b)
b = wx.Button(self, -1, "撤销选区", (225, 0))
self.Bind(wx.EVT_BUTTON, self.back_select, b)
b = wx.Button(self, -1, "《", (300, 0), size=(25, 25))
self.Bind(wx.EVT_BUTTON, self.previous, b)
b = wx.Button(self, -1, "》", (325, 0), size=(25, 25))
self.Bind(wx.EVT_BUTTON, self.next, b)
self.g1 = wx.Gauge(self, -1, 100, (0, 30), (-1, 100), wx.GA_VERTICAL)
def previous(self, evt):
if not hasattr(self, "pdfDoc"):
return
if self.i > 0:
self.i -= 1
self.change_pdf_page(self.i, False)
self.DoDrawing(-1)
if self.rects:
self.parent.SetTitle(self.path + "|" + self.extract_pdf_text())
def next(self, evt):
if not hasattr(self, "pdfDoc"):
return
if self.i self.i += 1
self.change_pdf_page(self.i, False)
self.DoDrawing(-1)
if self.rects:
self.parent.SetTitle(self.path + "|" + self.extract_pdf_text())
def back_select(self, evt):
if self.rects:
self.rects.pop()
self.DoDrawing(-1)
def OnButton(self, evt):
dlg = wx.FileDialog(
self, message="选择一个PDF文件",
defaultDir=os.getcwd(),
defaultFile="",
wildcard="PDF文件(*.pdf)|*.pdf",
wx.FD_CHANGE_DIR |
wx.FD_FILE_MUST_EXIST | wx.FD_PREVIEW
)
if dlg.ShowModal() == wx.ID_OK:
self.rects = []
path = dlg.GetPath()
self.pdfDoc = fitz.open(path)
self.i = 0
self.pageCount = self.pdfDoc.pageCount
self.change_pdf_page(self.i)
self.path = os.path.basename(path)
self.parent.SetTitle(self.path)
self.DoDrawing(-1)
dlg.Destroy()
def change_pdf_page(self, i, move=True):
page = self.pdfDoc[i]
rect = page.rect
print("pdf范围:", rect)
mat = fitz.Matrix(1, 1)
pix = page.get_pixmap(matrix=mat, alpha=False, clip=rect)
pix.save("tmp.png")
self.change_img("tmp.png", move)
def save_FileDialog(self, format="csv"):
dlg = wx.FileDialog(
self, message=f"保存一个{format}文件", defaultDir=os.getcwd(),
defaultFile="", wildcard=f"{format}文件(*.{format})|*.{format}", wx.FD_OVERWRITE_PROMPT
)
path = None
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
return path
def save_img(self, evt):
if not hasattr(self, "pdfDoc"):
return
dlg = wx.DirDialog(self, "选择图片保存的文件夹:",
wx.DD_DIR_MUST_EXIST
# | wx.DD_CHANGE_DIR
)
mat = fitz.Matrix(1, 1)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
for i in range(self.pdfDoc.pageCount):
page = self.pdfDoc[i]
clip = page.rect
pix = page.get_pixmap(matrix=mat, alpha=False, clip=clip)
if self.rects:
name = self.extract_pdf_text(page=page, rect=self.rects[-1])
else:
name = f"p{i:0>3d}"
pix.save(f"{path}/{name}.png")
self.g1.SetValue((i + 1) * 100 // self.pdfDoc.pageCount)
dlg.Destroy()
os.system(f"explorer {path}")
def save_file(self, evt):
if not hasattr(self, "pdfDoc"):
return
path = self.save_FileDialog()
if path is None:
return
data = []
for i in range(self.pdfDoc.pageCount):
page = self.pdfDoc[i]
row = [self.extract_pdf_text(page, rect)
for i, rect in enumerate(self.rects)]
data.append(row)
with open(path, "w") as f:
writer = csv.writer(f, lineterminator="\n")
row = [f"区域{i}" for i in range(1, len(row) + 1)]
writer.writerow(row)
for row in data:
writer.writerow(row)
os.system(f"cmd /c start {path}")
def extract_pdf_text(self, page=None, rect=None):
if page is None:
page = self.pdfDoc[self.i]
if rect is None:
rect = self.rects[-1]
a, b, c, d = rect
clip = fitz.Rect(a, b, a + c, b + d)
text = page.get_text(clip=clip).strip()
return text
def change_img(self, img_path, move=True):
self.bmp = wx.Bitmap(img_path)
self.SetSize(self.bmp.GetSize())
self.parent.SetSize(self.parent.GetBestSize())
if move:
self.parent.Center()
def DoDrawing(self, evt):
if not hasattr(self, "bmp"):
return
dc = wx.ClientDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
dc.SetPen(wx.Pen('blue'))
dc.SetBrush(wx.Brush('white', wx.BRUSHSTYLE_TRANSPARENT))
dc.DrawRectangleList(self.rects)
def OnLeftButtonEvent(self, event):
if event.LeftDown():
self.x, self.y = event.GetPosition()
self.rects.append([self.x, self.y, 0, 0])
elif event.Dragging():
x, y = event.GetPosition()
self.rects[-1][2] = x - self.x
self.rects[-1][3] = y - self.y
self.DoDrawing(-1)
elif event.LeftUp():
print(self.rects)
if self.rects[-1][2] <5 or self.rects[-1][3] <5:
self.rects.pop()
else:
self.parent.SetTitle(self.path + "|" + self.extract_pdf_text())
app = wx.App()
frm = wx.Frame(None)
pnl = MyCanvas(frm)
frm.Center()
frm.Show()
frm.SetTitle("PDF文本提取器")
app.MainLoop()

功能简介

打开软件后界面如下:

点击打开文件按钮打开之前的PDF文件后效果如下:

框选区域后,标题栏会自动显示当前框选的区域提取到的文字,还可以左右按钮切换:

实际我们需要提取文字的区域可能不止这一个,所以程序支持多区域框选:

完成区域框选后就可以点击保存文件,将PDF每页提取到的文本保存到一个csv文件中,当前选区的保存结果如下:

可以看到已经按框选顺序依次保存了每一个区域的字符串。

如果选择区域时发现提取结果不准确,可以撤销后重新选择:

保存图片则会将PDF的每页的整体保存为一张图片,未选择区域时,以页码为文件名保存图片:

选择区域时,会自动提取最后一个区域提取的文本作为当前页的文件名:

开发代码

当然这个项目由于本人是一次使用wxpython,功能非常简约,现在将完整代码开源出来期待各位大佬的改进。

源码和已编译工具下载地址:

https://codechina.csdn.net/as604049322/python_gui

完整代码:

"""
小小明的代码
CSDN主页:https://blog.csdn.net/as604049322
"""
__author__ = '小小明'
__time__ = '2021/11/24'
import csv
import wx
import os
import fitz
class MyCanvas(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.parent = parent
self.rects = []
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonEvent)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftButtonEvent)
self.Bind(wx.EVT_MOTION, self.OnLeftButtonEvent)
self.Bind(wx.EVT_PAINT, self.DoDrawing)
b = wx.Button(self, -1, "打开文件", (0, 0))
self.Bind(wx.EVT_BUTTON, self.OnButton, b)
b = wx.Button(self, -1, "保存文件", (75, 0))
self.Bind(wx.EVT_BUTTON, self.save_file, b)
b = wx.Button(self, -1, "保存图片", (150, 0))
self.Bind(wx.EVT_BUTTON, self.save_img, b)
b = wx.Button(self, -1, "撤销选区", (225, 0))
self.Bind(wx.EVT_BUTTON, self.back_select, b)
b = wx.Button(self, -1, "《", (300, 0), size=(25, 25))
self.Bind(wx.EVT_BUTTON, self.previous, b)
b = wx.Button(self, -1, "》", (325, 0), size=(25, 25))
self.Bind(wx.EVT_BUTTON, self.next, b)
self.g1 = wx.Gauge(self, -1, 100, (0, 30), (-1, 100), wx.GA_VERTICAL)
def previous(self, evt):
if not hasattr(self, "pdfDoc"):
return
if self.i > 0:
self.i -= 1
self.change_pdf_page(self.i, False)
self.DoDrawing(-1)
if self.rects:
self.parent.SetTitle(self.path + "|" + self.extract_pdf_text())
def next(self, evt):
if not hasattr(self, "pdfDoc"):
return
if self.i self.i += 1
self.change_pdf_page(self.i, False)
self.DoDrawing(-1)
if self.rects:
self.parent.SetTitle(self.path + "|" + self.extract_pdf_text())
def back_select(self, evt):
if self.rects:
self.rects.pop()
self.DoDrawing(-1)
def OnButton(self, evt):
dlg = wx.FileDialog(
self, message="选择一个PDF文件",
defaultDir=os.getcwd(),
defaultFile="",
wildcard="PDF文件(*.pdf)|*.pdf",
wx.FD_CHANGE_DIR |
wx.FD_FILE_MUST_EXIST | wx.FD_PREVIEW
)
if dlg.ShowModal() == wx.ID_OK:
self.rects = []
path = dlg.GetPath()
self.pdfDoc = fitz.open(path)
self.i = 0
self.pageCount = self.pdfDoc.pageCount
self.change_pdf_page(self.i)
self.path = os.path.basename(path)
self.parent.SetTitle(self.path)
self.DoDrawing(-1)
dlg.Destroy()
def change_pdf_page(self, i, move=True):
page = self.pdfDoc[i]
rect = page.rect
print("pdf范围:", rect)
mat = fitz.Matrix(1, 1)
pix = page.get_pixmap(matrix=mat, alpha=False, clip=rect)
pix.save("tmp.png")
self.change_img("tmp.png", move)
def save_FileDialog(self, format="csv"):
dlg = wx.FileDialog(
self, message=f"保存一个{format}文件", defaultDir=os.getcwd(),
defaultFile="", wildcard=f"{format}文件(*.{format})|*.{format}", wx.FD_OVERWRITE_PROMPT
)
path = None
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
return path
def save_img(self, evt):
if not hasattr(self, "pdfDoc"):
return
dlg = wx.DirDialog(self, "选择图片保存的文件夹:",
wx.DD_DIR_MUST_EXIST
# | wx.DD_CHANGE_DIR
)
mat = fitz.Matrix(1, 1)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
for i in range(self.pdfDoc.pageCount):
page = self.pdfDoc[i]
clip = page.rect
pix = page.get_pixmap(matrix=mat, alpha=False, clip=clip)
if self.rects:
name = self.extract_pdf_text(page=page, rect=self.rects[-1])
else:
name = f"p{i:0>3d}"
pix.save(f"{path}/{name}.png")
self.g1.SetValue((i + 1) * 100 // self.pdfDoc.pageCount)
dlg.Destroy()
os.system(f"explorer {path}")
def save_file(self, evt):
if not hasattr(self, "pdfDoc"):
return
path = self.save_FileDialog()
if path is None:
return
data = []
for i in range(self.pdfDoc.pageCount):
page = self.pdfDoc[i]
row = [self.extract_pdf_text(page, rect)
for i, rect in enumerate(self.rects)]
data.append(row)
with open(path, "w") as f:
writer = csv.writer(f, lineterminator="\n")
row = [f"区域{i}" for i in range(1, len(row) + 1)]
writer.writerow(row)
for row in data:
writer.writerow(row)
os.system(f"cmd /c start {path}")
def extract_pdf_text(self, page=None, rect=None):
if page is None:
page = self.pdfDoc[self.i]
if rect is None:
rect = self.rects[-1]
a, b, c, d = rect
clip = fitz.Rect(a, b, a + c, b + d)
text = page.get_text(clip=clip).strip()
return text
def change_img(self, img_path, move=True):
self.bmp = wx.Bitmap(img_path)
self.SetSize(self.bmp.GetSize())
self.parent.SetSize(self.parent.GetBestSize())
if move:
self.parent.Center()
def DoDrawing(self, evt):
if not hasattr(self, "bmp"):
return
dc = wx.ClientDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
dc.SetPen(wx.Pen('blue'))
dc.SetBrush(wx.Brush('white', wx.BRUSHSTYLE_TRANSPARENT))
dc.DrawRectangleList(self.rects)
def OnLeftButtonEvent(self, event):
if event.LeftDown():
self.x, self.y = event.GetPosition()
self.rects.append([self.x, self.y, 0, 0])
elif event.Dragging():
x, y = event.GetPosition()
self.rects[-1][2] = x - self.x
self.rects[-1][3] = y - self.y
self.DoDrawing(-1)
elif event.LeftUp():
print(self.rects)
if self.rects[-1][2] <5 or self.rects[-1][3] <5:
self.rects.pop()
else:
self.parent.SetTitle(self.path + "|" + self.extract_pdf_text())
app = wx.App()
frm = wx.Frame(None)
pnl = MyCanvas(frm)
frm.Center()
frm.Show()
frm.SetTitle("PDF文本提取器")
app.MainLoop()


推荐阅读
  • 利用决策树预测NBA比赛胜负的Python数据挖掘实践
    本文通过使用2013-14赛季NBA赛程与结果数据集以及2013年NBA排名数据,结合《Python数据挖掘入门与实践》一书中的方法,展示如何应用决策树算法进行比赛胜负预测。我们将详细讲解数据预处理、特征工程及模型评估等关键步骤。 ... [详细]
  • CentOS7源码编译安装MySQL5.6
    2019独角兽企业重金招聘Python工程师标准一、先在cmake官网下个最新的cmake源码包cmake官网:https:www.cmake.org如此时最新 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • Python自动化处理:从Word文档提取内容并生成带水印的PDF
    本文介绍如何利用Python实现从特定网站下载Word文档,去除水印并添加自定义水印,最终将文档转换为PDF格式。该方法适用于批量处理和自动化需求。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文介绍如何使用Python进行文本处理,包括分词和生成词云图。通过整合多个文本文件、去除停用词并生成词云图,展示文本数据的可视化分析方法。 ... [详细]
  • 本文详细介绍了Python中文件的基本操作,包括打开、读取、写入和关闭文件的方法,并通过实例展示了如何将Excel文件转换为CSV文件以及进一步转换为HTML文件。此外,还涉及了成绩等级替换的具体实现。 ... [详细]
  • 使用Pandas高效读取SQL脚本中的数据
    本文详细介绍了如何利用Pandas直接读取和解析SQL脚本,提供了一种高效的数据处理方法。该方法适用于各种数据库导出的SQL脚本,并且能够显著提升数据导入的速度和效率。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • XNA 3.0 游戏编程:从 XML 文件加载数据
    本文介绍如何在 XNA 3.0 游戏项目中从 XML 文件加载数据。我们将探讨如何将 XML 数据序列化为二进制文件,并通过内容管道加载到游戏中。此外,还会涉及自定义类型读取器和写入器的实现。 ... [详细]
  • 本文详细介绍了macOS系统的核心组件,包括如何管理其安全特性——系统完整性保护(SIP),并探讨了不同版本的更新亮点。对于使用macOS系统的用户来说,了解这些信息有助于更好地管理和优化系统性能。 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 本文介绍如何使用 Python 的 Pandas 库中 Series 对象的 round() 方法,对数值进行四舍五入处理。该方法在数据预处理和分析中非常有用。 ... [详细]
  • 本文介绍了在Java环境中使用PDFBox和XPDF工具从PDF文件中提取文本内容的方法。重点讨论了处理中文字符集及解决相关错误的技术细节,特别是针对某些特定格式的PDF文件(如网上填写的报名表和下载的论文)遇到的问题及解决方案。 ... [详细]
  • ABBYY FineReader:高效PDF转换、精准OCR识别与文档对比工具
    在处理PDF转换和OCR识别时,您是否遇到过格式混乱、识别率低或图表无法正常识别的问题?ABBYY FineReader以其强大的功能和高精度的识别技术,完美解决这些问题,帮助您轻松找到最终版文档。 ... [详细]
author-avatar
路易公子_352
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有