pdf 文件版面分析--pdfplumber (python 文档解析提取)

pdfplumber 的特点

1、它是一个纯 python 第三方库,适合 python 3.x 版本
2、它用来查看pdf各类信息,能有效提取文本、表格
3、它不支持修改或生成pdf,也不支持对pdf扫描件的处理

import glob
import pdfplumber
import re
from collections import defaultdict
import json

class PDFProcessor:
    def __init__(self, filepath):
        self.filepath = filepath
        #打开文档,注意存放的位置
        self.pdf = pdfplumber.open(filepath)
        self.all_text = defaultdict(dict)
        self.allrow = 0
        self.last_num = 0

    def check_lines(self, page, top, buttom):
    	# 文本数据
        lines = page.extract_words()[::]
        text = ''
        last_top = 0
        last_check = 0
        for l in range(len(lines)):
            each_line = lines[l]
            check_re = '(?:。|;|单位:元|单位:万元|币种:人民币|\d|报告(?:全文)?(?:(修订版)|(修订稿)|(更正后))?)$'
            if top == '' and buttom == '':
                if abs(last_top - each_line['top']) <= 2:
                    text = text + each_line['text']
                #elif last_check > 0 and (page.height * 0.85 - each_line['top']) > 0 and not re.search(check_re, text):
                elif last_check > 0 and (page.height * 0.9 - each_line['top']) > 0 and not re.search(check_re, text):

                    text = text + each_line['text']
                else:
                    text = text + '\n' + each_line['text']
            elif top == '':
                if each_line['top'] > buttom:
                    if abs(last_top - each_line['top']) <= 2:
                        text = text + each_line['text']
                    elif last_check > 0 and (page.height * 0.85 - each_line['top']) > 0 and not re.search(check_re,
                                                                                                          text):
                        text = text + each_line['text']
                    else:
                        text = text + '\n' + each_line['text']
            else:
                if each_line['top'] < top and each_line['top'] > buttom:
                    if abs(last_top - each_line['top']) <= 2:
                        text = text + each_line['text']
                    elif last_check > 0 and (page.height * 0.85 - each_line['top']) > 0 and not re.search(check_re,
                                                                                                          text):
                        text = text + each_line['text']
                    else:
                        text = text + '\n' + each_line['text']
            last_top = each_line['top']
            last_check = each_line['x1'] - page.width * 0.85

        return text

    def drop_empty_cols(self, data):
        # 删除所有列为空数据的列
        transposed_data = list(map(list, zip(*data)))
        filtered_data = [col for col in transposed_data if not all(cell is '' for cell in col)]
        result = list(map(list, zip(*filtered_data)))
        return result

    @staticmethod
    def keep_visible_lines(obj):
        """
        If the object is a ``rect`` type, keep it only if the lines are visible.

        A visible line is the one having ``non_stroking_color`` not null.
        """
        if obj['object_type'] == 'rect':
            if obj['non_stroking_color'] is None:
                return False
            if obj['width'] < 1 and obj['height'] < 1:
                return False
            # return obj['width'] >= 1 and obj['height'] >= 1 and obj['non_stroking_color'] is not None
        if obj['object_type'] == 'char':
            return obj['stroking_color'] is not None and obj['non_stroking_color'] is not None
        return True

    def extract_text_and_tables(self, page):
        buttom = 0
        page = page.filter(self.keep_visible_lines)
        tables = page.find_tables()
        if len(tables) >= 1:
        	# 表格数据
            count = len(tables)
            for table in tables:
                if table.bbox[3] < buttom:
                    pass
                else:
                    count -= 1
                    top = table.bbox[1]
                    text = self.check_lines(page, top, buttom)
                    text_list = text.split('\n')
                    for _t in range(len(text_list)):
                        self.all_text[self.allrow] = {'page': page.page_number, 'allrow': self.allrow,
                                                      'type': 'text', 'inside': text_list[_t]}
                        self.allrow += 1

                    buttom = table.bbox[3]
                    new_table = table.extract()
                    r_count = 0
                    for r in range(len(new_table)):
                        row = new_table[r]
                        if row[0] is None:
                            r_count += 1
                            for c in range(len(row)):
                                if row[c] is not None and row[c] not in ['', ' ']:
                                    if new_table[r - r_count][c] is None:
                                        new_table[r - r_count][c] = row[c]
                                    else:
                                        new_table[r - r_count][c] += row[c]
                                    new_table[r][c] = None
                        else:
                            r_count = 0

                    end_table = []
                    for row in new_table:
                        if row[0] != None:
                            cell_list = []
                            cell_check = False
                            for cell in row:
                                if cell != None:
                                    cell = cell.replace('\n', '')
                                else:
                                    cell = ''
                                if cell != '':
                                    cell_check = True
                                cell_list.append(cell)
                            if cell_check == True:
                                end_table.append(cell_list)
                    end_table = self.drop_empty_cols(end_table)

                    for row in end_table:
                        self.all_text[self.allrow] = {'page': page.page_number, 'allrow': self.allrow,
                                                      'type': 'excel', 'inside': str(row)}
                        # self.all_text[self.allrow] = {'page': page.page_number, 'allrow': self.allrow, 'type': 'excel',
                        #                               'inside': ' '.join(row)}
                        self.allrow += 1

                    if count == 0:
                        text = self.check_lines(page, '', buttom)
                        text_list = text.split('\n')
                        for _t in range(len(text_list)):
                            self.all_text[self.allrow] = {'page': page.page_number, 'allrow': self.allrow,
                                                          'type': 'text', 'inside': text_list[_t]}
                            self.allrow += 1

        else:
        	#文本数据
            text = self.check_lines(page, '', '')
            text_list = text.split('\n')
            for _t in range(len(text_list)):
                self.all_text[self.allrow] = {'page': page.page_number, 'allrow': self.allrow,
                                              'type': 'text', 'inside': text_list[_t]}
                self.allrow += 1

        first_re = '[^计](?:报告(?:全文)?(?:(修订版)|(修订稿)|(更正后))?)$'
        end_re = '^(?:\d|\\|\/|第|共|页|-|_| ){1,}'
        if self.last_num == 0:
            try:
                first_text = str(self.all_text[1]['inside'])
                end_text = str(self.all_text[len(self.all_text) - 1]['inside'])
                if re.search(first_re, first_text) and not '[' in end_text:
                    self.all_text[1]['type'] = '页眉'
                    if re.search(end_re, end_text) and not '[' in end_text:
                        self.all_text[len(self.all_text) - 1]['type'] = '页脚'
            except:
                print(page.page_number)
        else:
            try:
                first_text = str(self.all_text[self.last_num + 2]['inside'])
                end_text = str(self.all_text[len(self.all_text) - 1]['inside'])
                if re.search(first_re, first_text) and '[' not in end_text:
                    self.all_text[self.last_num + 2]['type'] = '页眉'
                if re.search(end_re, end_text) and '[' not in end_text:
                    self.all_text[len(self.all_text) - 1]['type'] = '页脚'
            except:
                print(page.page_number)

        self.last_num = len(self.all_text) - 1


    def process_pdf(self):
        for i in range(len(self.pdf.pages)):
            self.extract_text_and_tables(self.pdf.pages[i])


    def save_all_text(self, path):
        with open(path, 'w', encoding='utf-8') as file:
            for key in self.all_text.keys():
                file.write(json.dumps(self.all_text[key], ensure_ascii=False) + '\n')


def process_all_pdfs_in_folder(folder_path):
    file_paths = glob.glob(f'{folder_path}/*')
    file_paths = sorted(file_paths, reverse=True)

    for file_path in file_paths:
        print(file_path)
        try:
            processor = PDFProcessor(file_path)
            processor.process_pdf()
            save_path = 'RAG_ASMPLE_DATAS_TXTS/' + file_path.split('/')[-1].replace('.pdf', '.txt')
            processor.save_all_text(save_path)
        except:
            print('check')

if __name__ == '__main__':
    # 需要解析的pdf文件路径
    pdf_path = r'C:\Users\WWS\RAG_ASMPLE_DATAS\2020-02-26__上海爱旭新能源股份有限公司__600732__爱旭股份__2019年__年度报告.pdf'
    # pdf解析后的txt内容文件
    out_path = r'C:\Users\WWS\RAG_ASMPLE_DATAS\2020-02-26__上海爱旭新能源股份有限公司__600732__爱旭股份__2019年__年度报告.txt'
    processor = PDFProcessor(pdf_path)
    processor.process_pdf()
    processor.save_all_text(out_path)

参考

版面分析–PDF解析神器pdfplumber
版面分析–富文本txt读取

补充


提取PDF中的图片并保存到本地

import pdfplumber
file_name = "**.pdf"# 需要解析的pdf的文件路径
output_file = "**.xlsx" # pdf解析后的内容

with pdfplumber.open(file_name) as pdf:
	#获取第一页
	first_page = pdf.pages[1]
	print('页码:', first_page.page_number)
	print('page width:', first_page.width)
	print('page height:', first_page.height)
	# get the first page text
	text = first_page.extract_text()
	print(text)
	
	# 获取第一页图片,获取到的是一个列表,列表中存储的是字典
	imgs = first_page.images
	i = 0
	for img in imgs:
		# 获取图片的二进制流
		print(img['stream'].get_data())
		with open(output_file, mode='wb') as f2:
			f2.write(img['stream'].get_data())

提取pdf 表格文本,保存为excel文件

import pdfplumber
from openpyxl import Workbook# 保存表格,需要安装openpyxl
file_name = '**.pdf'
output_file = '**.xlsx'
with pdfplumber.open(file_name) as pdf:
	page01 = pdf.pages[0]
	table = page01.extract_table()
	workbook = Workbook()
	sheet = workbook.active
	for row in table:
		sheet.append(row)
	workbook.save(filename=output_file)

提取PDF表格 文本

import pdfplumber
file_name = '**.pdf'
output_file = '**.txt'
with pdfplumber.open(file_name) as p:
	page_count = len(p.pages)# 统计文档的页数
	for i in range(0, page_count):
		page = p.pages[i]# 提取每页的对象并存储
		textdata = page.extract_table()#提取每页的表格文字信息
		# table2 = page01.extract_tables()# 提取多个表格
		data = open(output_file , 'a') # 将 表格文字存放在需要存储的文档里面
		data.write(textdata )# 文档内容写入

提取PDF纯文本

import pdfplumber
file_name = '**.pdf'
output_file = '**.txt'
with pdfplumber.open(file_name) as p:
	page_count = len(p.pages)# 统计文档的页数
	for i in range(0, page_count):
		page = p.pages[i]# 提取每页的对象并存储
		textdata = page.extract_text()#提取每页的文字信息
		data = open(output_file , 'a') # 将 表格文字存放在需要存储的文档里面
		data.write(textdata )# 文档内容写入
	

读取富文本txt
python 读取文件函数有三种 read()、readline()、readlines()

  • read() 一次性读取所有文本
  • readline() 读取第一行的内容
  • readlines() 读取全部内容,以数列的格式返回
with open('rag_datas/story.txt', 'r', encoding='utf-8' ) as f:
	data = f.read()
	print(data)
with open('rag_datas/story.txt', 'r', encoding='utf-8' ) as f:
	data = f.readline()
	print(data)
with open('rag_datas/story.txt', 'r', encoding='utf-8' ) as f:
	for line in f.readlines():
		line = line.strip('\n')
		print(line)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

还有谁……想知道“线下与线上布局之间的本质区别”

还有谁……想知道 线下与线上布局之间的本质区别 hello,亲爱的你们好.… 我是你们的好朋友,正博,今天是非常特殊的一天,给每一位读者准备了一份特殊的神秘礼物…… 在分享【特殊礼物】之前,请允许我分享一下《线下营销与线上销售的9大核心差异》…… 1、注意力; 2、销…

nginx--反向代理

反向代理 指的是代理外网用户的请求到内部的指定web服务器器&#xff0c;并将数据返回给用户的一种方式&#xff0c;这是用的比较多的一种方式 模块和功能 ngx_http_proxy_module&#xff1a; 将客户端的请求以http协议转发至指定服务器进行处理。ngx_stream_proxy_module&…

我从这些书籍中学来的财务以及税务知识

“你不能指望在开始工作的头两年攒下任何积蓄。” 这句话一直是我的座右铭&#xff0c;也是我给大学生的个人理财建议。这也就难怪我二十出头的时候&#xff0c;基本就是靠薪水过日子。 回想起来&#xff0c;我意识到其实这并不是最好的建议&#xff0c;甚至非常不好。 我现…

MySQL卸载

目录 MySQL卸载 1.关闭服务 2.卸载软件 3.清理文件夹 4.清理注册表 5.重启电脑 说明 上一篇文章介绍了数据库的一些基本概念&#xff0c;以及MySQL的数据模型&#xff0c;并在文章末尾附上了MySQL的下载地址&#xff0c;但是对于已经安装过MySQL的同学&#xff0c;如果需…

【小菜鸟之---Ansible基础详解】

文章目录 1 【Ansible简介】1.1简介1.2 Ansible 特点1.3 Ansible的工作机制1.4Ansible任务工作模式 2【安装部署】2.1安装命令2.2 Ansible配置文件2.3主机清单配置2.4 基于ssh免密登录2.5常用命令 3【Ansible常用模块】3.1 ping模块3.2 shell模块3.3 command模块3.4 copy模块3.…

力扣100284. 有效单词(C++)

【题解】 (实际在力扣中运行的代码只需要把下方的check函数放到力扣作答区给的模板中就可以) #include <bits/stdc.h> #include <iostream> #include <vector> #include <string> #include <cctype> #include <cstring> #include <st…

【机器学习】机器学习在教育领域的应用场景探索

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

漫谈音频深度伪造技术

作为人工智能时代的新型媒体合成技术&#xff0c;深度伪造技术近年来在网络媒体中的涉及领域越发广泛、出现频次越发频繁。据路透社报道&#xff0c;2023年&#xff0c;社交媒体网站上发布50万个深度伪造的语音和视频。 1、深度伪造技术的五个方面 音频深度伪造技术&#xff…

Spring拦截器

一、简介&#xff1a; Spring Boot 拦截器是面向切面编程-----AOP 的具体实现&#xff0c;用于对请求做预处理。 1.1.什么是拦截器&#xff1a;在AOP&#xff08;Aspect-Oriented Programming&#xff09;中用于在某个方法或字段被访问之前&#xff0c;进行拦截然后在之前或之…

华为二层交换机与路由器连通上网实验

华为二层交换机与路由器连通上网实验 二层交换机是一种网络设备&#xff0c;用于在局域网&#xff08;LAN&#xff09;中转发数据帧。它工作在OSI模型的第二层&#xff0c;即数据链路层。二层交换机通过学习和维护MAC地址表&#xff0c;实现了数据的快速转发和广播域的隔离。 实…

读天才与算法:人脑与AI的数学思维笔记19_深度数学

1. 深度数学 1.1. 组合与选择&#xff0c;是发明新事物的两个不可或缺的条件 1.1.1. 保尔瓦雷里&#xff08;Paul Valry&#xff09; 1.2. 利用以往的数学定理证明过程训练算法&#xff0c;以发现新的定理 1.3. 谷歌设在伦敦的总部整体有一种现代牛津大学的感觉&#xff0c…

17_Scala面向对象高阶功能

文章目录 1.继承1.1 构造对象时,父类对象优于子类对象1.2父类主构造有参数,子类必须要显示地调用父类主构造器并传值 2.封装3.抽象3.1抽象定义3.2子类继承抽象类3.3抽象属性 4.伴生对象4.1创建类和伴生对象4.2调用 1.继承 –和Java一样,权限protected , public.父类定义子类用…

【Java】基本程序设计结构(二)

前言&#xff1a;上一篇我们详细介绍了Java基本程序设计结构中前半部分&#xff0c;一个简单的Java应用&#xff0c;注释&#xff0c;数据类型&#xff0c;变量与常量&#xff0c;运算符&#xff0c;字符串。包括本篇将延续上篇内容介绍后续内容&#xff0c;包括输入输出&#…

UE5 UMG

锚点 参考链接&#xff1a;虚幻5UI系统&#xff08;UMG&#xff09;基础&#xff08;已完结&#xff09;_哔哩哔哩_bilibili

政安晨:【Keras机器学习示例演绎】(三十七)—— 在计算机视觉中学习调整大小

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本文目标&#xff1a;在计算机视觉中学习调整大小…

数据结构(十一)----图的应用

目录 一.最小生成树 1.Prim算法&#xff08;普里姆&#xff09; 2.Kruskal算法(克鲁斯卡尔): 二.最短路径&#xff08;BFS算法&#xff09; 1.单源最短路径 &#xff08;1&#xff09;BFS算法&#xff08;无权图&#xff09; &#xff08;2&#xff09;Dijkstra算法&…

QT+网络调试助手+TCP客户端

一、网络调试助手UI界面 编程主要思路&#xff1a; 首先将水平的控件 水平布局 &#xff0c;然后相对垂直的控件 垂直布局 &#xff0c;哪怕是底下的groupBox也需要和里面的内容 水平布局&#xff0c;然后最后框选全部 栅格布局。如果需要界面自适应窗口大小&#xff0c…

JavaScript js写九九乘法表(两种方法)

方法一&#xff1a; 观察规律&#xff1a; 第一个数每行都是自增1。 我们发下第二个数都是从1开始&#xff0c;依次递增1&#xff0c;永远不大于前面的数。 前面数字每自增一次&#xff0c;后面数字自增一轮。 我们可以用双重for循环&#xff0c;外层初始值设为i&#xff0…

【C++】对文章分词,并对词频用不同排序方法排序,比较各排序算法效率

文章分词 1&#xff0e;问题描述2&#xff0e;需求分析3&#xff0e;概要设计3.1 主程序流程3.2 函数调用关系 4&#xff0e;主函数实现4.1 main.h4.2 main.cpp 5. 函数实现5.1 processDic函数5.2 forwardMax函数5.3 countWordFreq函数5.4 quickResult函数5.5 其它排序算法效率…

【链表】:链表的带环问题

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;数据结构 &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 链表的带环问题在链表中是一类比较难的问题&#xff0c;它对我们的思维有一个比较高的要求&#xff0c;但是这一类…
最新文章