python pdfplumber优化表格提取

news2024/10/7 18:31:54

样例pdf

直接使用文本提取效果:

使用表格提取

 

根据提取的文本信息是没办法获取到表格数据的,太乱了。尤其是 3 4列。

解决:

自行画线,根据画线进行提取。

效果:

思路:

1.根据表头进行画竖线

2.根据行坐标画横线

3.根据坐标放入单元格的list中

4.拼接单元格文字。

问题:

根据表头画竖线,可能内容超出表头左右坐标。

解决办法:根据内容进行特殊匹配。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import re
import pdfplumber
import logging as log


class PDF(object):
    file_path = None
    config = {}
    bill_date_begin = None
    bill_date_end = None
    parse_data = []
    unit = None
    trans_during = None

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls)

    def parse(self):
        try:

            with pdfplumber.open(self.file_path) as pdf:
                page_ind = 1
                log.info(" 发现总页数:{}".format(str(page_ind)))

                for index, page in enumerate(pdf.pages):
                    print(" 第 " + str(page_ind) + " 页: ")
                    page_ind += 1

                    explicit_vertical_lines = []
                    explicit_horizontal_lines = []

                    explicit_horizontal_lines_y_line = []
                    explicit_horizontal_lines_h_line = []
                    explicit_horizontal_lines_keys = []
                    lines_dict = {}
                    table_begin = False

                    ts_y_list = {"jyzy": {"y": [-1, -1]}}
                    table_top = None
                    for ind, char in enumerate(page.chars):
                        next3Text = ""
                        if ind <= (len(page.chars) - 3):
                            next3Text = page.chars[ind]["text"] + page.chars[ind + 1]["text"] + page.chars[ind + 2]["text"]
                            if next3Text.find("交易日") > -1:
                                table_begin = True

                        if table_begin is False:
                            continue

                        if ind >= 2:
                            text = page.chars[ind - 2]["text"] + page.chars[ind - 1]["text"] + page.chars[ind]["text"]
                            if text.find("易日期") > -1 or text.find("出金额") > -1 or text.find("入金额") > -1 or text.find("户余额") > -1 or text.find("账标识") > -1 or text.find("户序号") > -1:
                                # print(text)
                                lines_dict = char
                                explicit_vertical_lines.append({
                                    "x0": lines_dict["x1"] + 2,
                                    "x1": lines_dict["x1"] + 2,
                                    "y0": lines_dict["y0"],
                                    "top": lines_dict["top"] - 5,
                                    "bottom": lines_dict["bottom"] + 700,
                                    "height": lines_dict["height"],
                                    "orientation": "v",
                                    "object_type": "line",
                                    "page_number": index
                                })
                                table_top = lines_dict["top"] - 5
                                explicit_horizontal_lines_h_line.append(lines_dict["x1"] + 2)

                            elif text.find("交易日") > -1:
                                # print(text)
                                lines_dict = page.chars[ind - 2]
                                put_dic = {
                                    "x0": lines_dict["x0"] - 8,
                                    "x1": lines_dict["x0"] - 8,
                                    "y0": lines_dict["y0"],
                                    "top": lines_dict["top"] - 5,
                                    "bottom": lines_dict["bottom"] + 700,
                                    "height": lines_dict["height"],
                                    "orientation": "v",
                                    "object_type": "line",
                                    "page_number": index
                                }

                                explicit_vertical_lines.append(put_dic)
                                explicit_horizontal_lines_h_line.append(lines_dict["x0"] - 8)
                            elif text.find("被冲账") > -1:
                                # print(text)
                                lines_dict = page.chars[ind - 2]
                                explicit_vertical_lines.append({
                                    "x0": lines_dict["x0"] - 1,
                                    "x1": lines_dict["x0"] - 1,
                                    "y0": lines_dict["y0"],
                                    "top": lines_dict["top"] - 5,
                                    "bottom": lines_dict["bottom"] + 700,
                                    "height": lines_dict["height"],
                                    "orientation": "v",
                                    "object_type": "line",
                                    "page_number": index
                                })
                                explicit_horizontal_lines_h_line.append(lines_dict["x0"] - 8)

                            # 竖线修复
                            if text.find("易摘要") > -1 or text.find("对方户") > -1:
                                if text.find("易摘要") > -1:
                                    ts_y_list["jyzy"]["y"][0] = char["x1"] + 2
                                    # ts_y_list["jyzy"]["y"][0] = char["x1"] + char["x1"] - char["x0"]
                                elif text.find("对方户") > -1:
                                    ts_y_list["jyzy"]["y"][1] = page.chars[ind-2]["x0"] - 1
                                    ts_y_list["jyzy"]["mid"] = page.chars[ind-2]

                            # 判断是否添加过该横线
                            if char["y0"] not in explicit_horizontal_lines_keys:
                                text = page.chars[ind - 2]["text"] + page.chars[ind - 1]["text"] + page.chars[ind]["text"]
                                if text.find("标识T") > -1 or text.find("leA") > -1:
                                    explicit_horizontal_lines_keys.append(char["y0"])
                                    continue

                                # 特殊竖线,根据表头坐标和内容坐标对比,取最左的那一个作为竖线的坐标
                                if ts_y_list["jyzy"]["y"][0] != -1 and ts_y_list["jyzy"]["y"][1] != -1 and char["x0"] > ts_y_list["jyzy"]["y"][0] and char["x1"] < ts_y_list["jyzy"]["y"][1]:
                                    if "mid" in ts_y_list["jyzy"].keys():
                                        if ts_y_list["jyzy"]["mid"]["x0"] > char["x0"]:
                                            ts_y_list["jyzy"]["mid"] = char
                                    else:
                                        ts_y_list["jyzy"]["mid"] = char

                                lines_dict_h = char

                                if text.find("ag2") > -1:
                                    explicit_horizontal_lines.append({
                                        "x0": 579,
                                        "x1": lines_dict_h["x1"] - 15,
                                        "y0": lines_dict_h["y0"],
                                        "y1": lines_dict_h["y1"],
                                        "top": lines_dict_h["bottom"] + 5,
                                        "bottom": lines_dict_h["bottom"] + 5,
                                        "height": lines_dict_h["height"],
                                        "width": lines_dict_h["width"],
                                        "orientation": "h",
                                        "object_type": "line",
                                        "page_number": index
                                    })
                                    explicit_horizontal_lines_y_line.append(lines_dict_h["bottom"] + 5)
                                elif next3Text.find("交易日") > -1 or (text.find("交易日") > -1 and index > 0):
                                    explicit_horizontal_lines.append({
                                        "x0": 579,
                                        "x1": lines_dict_h["x1"] - 15,
                                        "y0": lines_dict_h["y0"] if index == 0 else (lines_dict_h["y0"] - 5),
                                        "y1": lines_dict_h["y1"],
                                        "top": lines_dict_h["top"] - 5,
                                        "bottom": lines_dict_h["top"] - 5,
                                        "height": lines_dict_h["height"],
                                        "width": lines_dict_h["width"],
                                        "orientation": "h",
                                        "object_type": "line",
                                        "page_number": index
                                    })
                                    explicit_horizontal_lines_y_line.append(lines_dict_h["top"] + 5)
                                else:

                                    if lines_dict_h["x1"] > 200:
                                        continue

                                    # 正常行数据添加横线
                                    explicit_horizontal_lines.append({
                                        "x0": 579, # 横线长度
                                        "x1": lines_dict_h["x1"] - 12,
                                        "y0": lines_dict_h["y0"],
                                        "y1": lines_dict_h["y1"],
                                        "top": lines_dict_h["bottom"] + 5,
                                        "bottom": lines_dict_h["bottom"] + 5,
                                        "height": lines_dict_h["height"],
                                        "width": lines_dict_h["width"],
                                        "orientation": "h",
                                        "object_type": "line",
                                        "page_number": index
                                    })
                                    explicit_horizontal_lines_y_line.append(lines_dict_h["bottom"] + 5)

                                explicit_horizontal_lines_keys.append(char["y0"])

                    for k, v in ts_y_list.items():
                        if "mid" in v.keys() and v["mid"]["x0"] != -1:
                            lines_dict = v["mid"]
                            explicit_vertical_lines.append({
                                "x0": lines_dict["x0"] -8,
                                "x1": lines_dict["x0"] -8,
                                "y0": lines_dict["y0"],
                                "top": table_top,
                                "bottom": lines_dict["bottom"] + 700,
                                "height": lines_dict["height"],
                                "orientation": "v",
                                "object_type": "line",
                                "page_number": index
                            })
                            explicit_horizontal_lines_h_line.append(lines_dict["x0"] - 1)

                    page.curves.clear()
                    page.lines.clear()
                    # 赋值画线
                    page.objects['line'] = explicit_vertical_lines + explicit_horizontal_lines

                    explicit_horizontal_lines_h_line.sort()

                    line_text = []
                    # 根据横线坐标 & 纵线坐标 ,判断单元格
                    for ind, y in enumerate(explicit_horizontal_lines_y_line):
                        if (ind + 1) == len(explicit_horizontal_lines_y_line):
                            continue

                        # 单元格内容组装
                        def get_this_cell_text(y, next_y):
                            this_y_line = []
                            for char in page.chars:
                                if char["bottom"] > y and char["bottom"] < next_y:
                                    this_y_line.append(char)

                            cell_list = []
                            for ind_h, h in enumerate(explicit_horizontal_lines_h_line):
                                if (ind_h + 1) == len(explicit_horizontal_lines_h_line):
                                    continue

                                next_h = explicit_horizontal_lines_h_line[ind_h+1]
                                cell_text = ""
                                for char in this_y_line:
                                    if char["x0"] > h and char["x0"] < next_h:
                                        cell_text += char["text"]

                                cell_list.append(cell_text)
                            return cell_list

                        next_y = explicit_horizontal_lines_y_line[ind + 1]
                        this_cell_text = get_this_cell_text(y, next_y=next_y)
                        line_text.append(this_cell_text)

                    # im = page.to_image()
                    # im.draw_rects(page.extract_words())
                    # im.draw_vline(location, stroke={color}, stroke_width=1)
                    # im.save("aa.png')

                    # 合并解析的数据
                    self.parse_data = self.parse_data + line_text

            # 开始进行数据格式化
            data_list = []
            head_index = {'交易日期': -1, '账户序号': -1, '收入金额': -1, '支出金额': -1, '账户余额': -1, '交易摘要': -1, '对方户名': -1,
                          '被冲账标识': -1}
            isTableRow = True
            for ind, line in enumerate(self.parse_data):

                if ind == 0:
                    for ind_c, cell in enumerate(line):
                        for k, v in head_index.items():
                            if cell.find(k) > -1:
                                head_index[k] = ind_c

                    continue

                if "".join(s for s in line).find("第") > -1 and "".join(s for s in line).find("页") > -1:
                    continue
                if "".join(s for s in line).find("交易日期Transactiodate") > -1 and "".join(s for s in line).find("账户序号nAccountSeqNo") > -1:
                    continue

                if line[0] == "":
                    continue

                if "".join(s for s in line).find("声明:") > -1:
                    isTableRow = False

                if isTableRow is False:
                    continue

                value_dict = {}
                for k, v in head_index.items():
                    value_dict[k] = line[head_index[k]]
                # print(value_dict)

                currency = ""
                amount = value_dict["收入金额"].replace(" ", "") if value_dict["收入金额"] != "" else value_dict["支出金额"].replace(" ", "")
                balance_v = value_dict["账户余额"].replace(" ", "")

                if balance_v is not None and len(balance_v) > 0:
                    balance_v = balance_v.replace(currency, "")

                amount = amount.replace(currency, "")

                data_one = {"trans_remark": value_dict["交易摘要"],
                            "currency": self.unit,
                            "trans_date":value_dict["交易日期"],
                            "trans_amount": amount,
                            "balance": balance_v,
                            "trans_place": '',
                            "opponent_mess": value_dict["对方户名"]}

                data_list.append(data_one)

            import json
            print(json.dumps(data_list, ensure_ascii=False))
            self.parse_data = data_list
        except FileNotFoundError:
            raise FileNotFoundError
        except:
            raise Exception


PDF(None, "3.pdf", "123432", {}).parse()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1889381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3 ~ pinia学习

先看两个图 一个vuex 一个pinia 根据图看出来 pinia更简单了 那么具体怎么操作呢 我们来看下~ 第一步 下载 yarn add pinia # 或者使用 npm npm install pinia 第二步 注册 创建一个 pinia 实例 (根 store) 并将其传递给应用&#xff1a; import { createApp } from v…

vue的$nextTick是什么是干什么用的?

为什么需要使用$nextTick&#xff1f;他的使用场景 1.你在接口返回之后在获取高度 正常等页面加载在mounted这个钩子函数里 这时候就需要找到接口赋值的地方 这样就可以获取到数据操作以后的dom元素了 $nextTick是什么是干什么用的&#xff1f; $nextTick() 是 Vue.js 框…

超详细:安装Linux系统、虚拟现实教程

文章目录 一、如何下载并使用VMware虚拟机1.百度搜索vmware2.进入官网点击Workstation Pro链接3.博通注册对应的账号4.博通填写用户名、密码后直接登录会跳转到博通登录页5.个人使用选择个人版 二、国内镜像网站下载&#xff08;Centos版本&#xff09;三、镜像系统的安装1.打开…

【等保2.0的内容有哪些?】

“在“等保2.0”的基础上&#xff0c;分别增加了云计算安全、移动互联安全、物联网安全、工控系统安全、大数据安全5个拓展需求。 《中华人民共和国刑法》第253条&#xff0c;非法将公民个人资料卖给他人&#xff0c;并处罚金。 违反国家相关法律法规&#xff0c;将其在执行公…

计算机系统基础(三)

1.程序转换概述 机器指令和汇编指令 机器指令与汇编指令意义对应&#xff0c;都是机器级指令 汇编指令 如&#xff1a;M[R[bx]R[di]-6]←R[cl] R&#xff1a;寄存器内容 M&#xff1a;存储单元内容 机器指令 高级语言转换为机器代码的过程 根据计算机系统基础&#xff…

KEYSIGHT是德科技 E5063A ENA 系列网络分析仪

E5063A ENA 矢量网络分析仪 18GHz 2端口 降低无源射频元器件的测试成本 Keysight E5063A ENA 是一款经济适用的台式矢量网络分析仪&#xff0c;可用于测试简单的无源元器件&#xff0c;例如频率最高达到 18 GHz 的天线、滤波器、电缆或连接器。 作为业界闻名的 ENA 系列…

MLLM QLoRA微调实战:基于最新的袖珍Mini-InternVL模型

引言 大型语言模型&#xff08;LLM&#xff09;的世界正在不断发展&#xff0c;新的进步正在迅速出现。一个令人兴奋的领域是多模态LLM&#xff08;MLLMs&#xff09;的发展&#xff0c;这种模型既能够理解文本又能够理解图像&#xff0c;并与之进行交互。因此&#xff0c;这种…

ATG-2032:功率信号源的类型及应用领域简介

功率信号源是一种产生稳定、精确且可调节的电力信号的仪器&#xff0c;其主要作用是为测试和校准各种电子设备提供标准信号&#xff0c;以确保设备的精度和稳定性。 图&#xff1a;ATG-2000系列功率信号源 以下是功率信号源的主要类型和作用&#xff1a; 直流功率信号源 直流功…

Unity游戏帧率查看软件Fraps

Download Fraps 3.5.99 free version 下载、安装、运行这个软件&#xff0c;左上角就会自动显示帧率

SpringBoot的自动配置核心原理及拓展点

Spring Boot 的核心原理几个关键点 约定优于配置&#xff1a; Spring Boot 遵循约定优于配置的理念&#xff0c;通过预定义的约定&#xff0c;大大简化了 Spring 应用程序的配置和部署。例如&#xff0c;它自动配置了许多常见的开发任务&#xff08;如数据库连接、Web 服务器配…

python提取图片中的文字写入excel文件,并打包为exe可执行文件

python提取图片数据写入excel&#xff0c;并打包为exe可执行文件 1. 以下面的图片为例2. python环境需要的依赖包3. 创建交互式窗口4. 读取文件夹下的所有文件并提取数据5. 提取图片中字段的代码6. 打包代码为exe可执行文件安装打包依赖文件运行打包代码 1. 以下面的图片为例 2…

大数据------JavaWeb------会话跟踪技术(Cookie、Session)(完整知识点汇总)

会话跟踪技术&#xff08;Cookie&Session&#xff09; 注意&#xff1a; HTTP协议是无状态 的&#xff0c;即每次浏览器向服务器请求时&#xff0c;服务器都会将该请求视为新的请求&#xff0c;因此我们需要会话跟踪技术来实现会话内的数据共享 会话 当用户打开浏览器&am…

基于STM32的智能仓储温湿度监控系统

目录 引言环境准备智能仓储温湿度监控系统基础代码实现&#xff1a;实现智能仓储温湿度监控系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统实现4.4 用户界面与数据可视化应用场景&#xff1a;温湿度监控与管理问题解决方案与优化收尾与总结 1. 引言 智能仓储温湿度监…

【论文解读】Multiagent Multitraversal Multimodal Self-Driving: Open MARS Dataset

Open MARS Dataset 摘要引言Dataset CurationVehicle SetupData CollectionDataset Statistics Benchmark Task and ModelPlace RecognitionNeural Reconstruction Experimental ResultsVisual Place RecognitionNeural Reconstruction Opportunities and Challenges结论 摘要 …

echarts实现3D柱状图(视觉层面)

一、第一种效果 效果图 使用步骤 完整实例&#xff0c;copy就可直接使用 <template><div :class"className" :style"{height:height,width:width}" /> </template><script>import echarts from echartsrequire(echarts/theme/…

RAG技术下的文档智能检索

在数字化浪潮的推动下&#xff0c;信息检索已成为我们日常生活中不可或缺的一部分。然而&#xff0c;随着数据量的爆炸式增长&#xff0c;如何快速精准地从海量文档中检索出有价值的信息&#xff0c;成为了一个巨大的挑战。本文将带您走进 Pinecone 向量数据库的世界&#xff0…

计算机图形学games101——MVP

首先记得一个知识点 在旋转矩阵中&#xff0c;旋转矩阵的逆矩阵就是旋转矩阵的转置&#xff0c;这个矩阵是正交矩阵 我们需要做到的就是观测变换&#xff0c;这个变换包括视图变换和投影变换&#xff08;投影变换包含正交变换和透视变换&#xff09; 三维变换复习 首先复习…

Ubuntu20.04配置TurtleBot3 Waffle Pi远程控制

这里写目录标题 0. 机器人配置1. Ubuntu20.04配置TurtleBot3 Waffle Pi远程控制1.1 TurtleBot3 Waffle Pi端配置1.2 PC端配置1.2.1 安装turtlebot3的环境配置1.2.2 创建项目并安装Turtlebot31.2.3 配置环境变量 1.3 PC端与TurtleBot3进行通信1.3.1 PC端与机器人端互PING和SSH连…

ATA-L2水声功率放大器驱动水声换能器的测试研究

随着水声通信技术的发展&#xff0c;水下通信设备也开始逐步走向实用化&#xff0c;为了满足其实际的使用要求&#xff0c;功率放大器的设计需要具有高效率的特性&#xff0c;并能在水下长时间连续可靠的工作。 压电陶瓷换能器主要负责电信号与声信号之间的转换&#xff0c;换能…

ruoyi-cloud登录接口实现滑块验证码

一、前言 ruoyi项目默认的验证码是这样的 今天来尝试增加滑块验证码&#xff0c;我们用到的是tianai-captcha。 文档地址&#xff1a;http://doc.captcha.tianai.cloud/ 源码地址&#xff1a;https://gitee.com/tianai/tianai-captcha 下面来看具体的步骤。 二、后端 在g…