基于Python实现PDF区域文本提取工具
2022-11-15 canyinms.com 【 字体:大 中 小 】
打开软件后界面如下:
点击打开文件按钮打开之前的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.pageCount - 1: 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", style=wx.FD_OPEN | 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}", style=wx.FD_SAVE | 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, "选择图片保存的文件夹:", style=wx.DD_DEFAULT_STYLE # | 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()
猜你喜欢
2023年高考需注意这些事项 注意事项有哪些?
2023年高考时间是几月几日 全国高考什么时候?
高考倒计时1天 几号开始高考?
晋江8个考点实行交通管制 此举有何意义?
高考生哪六样东西千万别发朋友圈?
黑龙江高考2023时间安排表 黑龙江高考是全国几卷?
《教育资源免费送!开学季大放送》
小学教育,如何激发孩子学习兴趣?
了解最新职业培训趋势,掌握未来就业机会!
教如何填报高考志愿
橘子洲旅游攻略 长沙橘子洲怎么玩
西安周边旅游攻略 西安周边最值得去的地方
毛坦厂中学万人雨中送考 毛坦厂送考头车车牌91666祝福考生
珠峰被救女子不愿支付全部救援费用 珠峰攀登者为救遇险女子放弃登顶
登珠峰获救女子只愿承担4成救援费是怎么回事 登珠峰获救女子只愿承担4成救援费具体情况
国内出现偏肺病毒感染 尚无治疗药物
男子连吃100天麦当劳减重53斤是真的吗 男子连吃100天麦当劳为什么能减重53斤
本周狗屎运最旺的星座 本周狗屎运最旺的四大星座
印度耗资百亿卢比的大桥又塌了是什么情况 印度耗资百亿卢比的大桥又塌了是为什么
一个村考出300多大学生6个北大 湖南一小村考上一本奖4000二本3000