OCR提取PDF中的某字段,并用字段对文件进行命名,适用于所有图片类型文件

本文介绍了一种使用OCR技术批量处理PDF文件的方法,通过对PDF文件进行裁剪和字符识别,自动提取关键信息,并根据这些信息对文件进行重命名。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、需求如下:

二、思路

三、代码

3.1 导入包,并定义存放文件的路径与文件夹。

3.2 获取PDF文件

3.3 OCR识别提取字段

3.4 重命名并移动文件

四、改进

4.1 减少耗时

4.2 提升准确率

五、完整代码


一、需求如下:

数以万计pdf文件,其内容包含了公司的名称、统一社会信用代码等信息,如何对pdf进行识别,把pdf文件名更改为统一社会信用代码或企业名称?

 

二、思路

1. 首先肯定是想办法把pdf识别出来,变成文字信息,想必大家一定想到了使用OCR(optical character recognition)的方法,这里我使用的是百度paddlepaddle框架的paddleocr;paddleocr对pdf就可以进行识别,省去了转成图片文件的麻烦。

2. 第二步,对于识别出的字段,把我们想要的字段提取出来,经过测试,使用正则表达式是比较稳妥的方法。

3.最后将提取到的字段重命名成文件名即可。

三、代码

3.1 导入包,并定义存放文件的路径与文件夹。

import os.path
import re
import shutil
import time
from paddleocr import PaddleOCR

# 需要的目录
dirs = ['finished', 'pdf']
for d in dirs:
    if not os.path.exists(f'./{d}'):
        os.makedirs(f'./{d}')

# 需要的路径
PDF_PATH = './pdf'
SAVE_PATH = './finished'

3.2 获取PDF文件

st1 = time.perf_counter()

for file_name in os.listdir(PDF_PATH):  # 返回pdf目录下的文件夹名称
    pdf_names = os.listdir(rf'{PDF_PATH}/{file_name}')  # 包含pdf所有文件名组成的列表
    pdf_dir = os.path.join(PDF_PATH, file_name)  # pdf路径名 + 下面的文件名
    finished_path = os.path.join(SAVE_PATH, file_name)  # 完成时存储的路径,finished + pdf文件夹名

这部分注释写的很详细,注意st1是用于计算开始时间的。

3.3 OCR识别提取字段

# 调用PaddleOCR进行识别
OCR = PaddleOCR(use_angle_cls=True, lang='en')
result_ocr = OCR.ocr(pdf_crop_save, cls=True)
credit_code = re.findall('[0-9A-Z]{18}', str(result_ocr))

这就是paddleocr强大的地方,只需要三行代码,就可以完成识别。字段是通过正则表达式进行提取的,提取规则为:连续18位的数字与字母组合

3.4 重命名并移动文件

if credit_code:
    credit_code = credit_code[0]
    credit_code = replace_str(credit_code)

    if not os.path.exists(f'./finished/{file_name}'):
        os.makedirs(f'./finished/{file_name}')

    if os.path.exists(rf'{finished_path}/{credit_code}.pdf'):
        print(f'文件名称存在重复,{credit_code}')
        continue

    shutil.move(rf'{pdf_dir}/{pdf_name}', rf'{finished_path}/{credit_code}.pdf')
    print('完成文件移动:', credit_code)
    st2 = time.perf_counter()
    print('累计耗时:', st2 - st1)

else:
    print('识别失败:', credit_code, '累计耗时:', time.perf_counter() - st1)

这部分也没什么需要说的,最后把消耗的时间输出一下就好(其实不输出也没影响),主要就是记录下时间。

四、改进

4.1 减少耗时

有的读者可能会发现,一个pdf文件的文字可能上千字,而我们需要的仅仅是文件的几行内容。是否可以对pdf文件进行裁剪,直接识别我们所需的区域?

答案是可以的,PyPDF2为我们提供了方法:

# 定义用于裁剪的函数
def split(page, tup):
    page.mediaBox.lowerLeft = (tup[0], tup[1])
    page.mediaBox.lowerRight = (tup[2], tup[1])
    page.mediaBox.upperLeft = (tup[0], tup[3])
    page.mediaBox.upperRight = (tup[2], tup[3])

# 打开与关闭操作
output_file = PyPDF2.PdfFileWriter()
inputStream = open(pdf_name_path, 'rb')
input_file = PyPDF2.PdfFileReader(inputStream)

# pdf处理操作
this_page = input_file.getPage(0)  # 获取第1页
split(this_page, (60, 700, 500, 800))
output_file.addPage(this_page)
pdf_crop_save = rf'{CROP_PATH}/{pdf_name}'  # pdf_crop + pdf文件名
outputStream = open(pdf_crop_save, "wb")
output_file.write(outputStream)  # 将切割的文件存储在pdf_crop文件下

注释我写的非常详细了,就不在过多赘述了。

4.2 提升准确率

在识别统一社会信用代码时,会发现ocr会将l识别成1,0识别成O的情况,但实际l和O都是不会在统一社会信用代码中存在的,我们可以用replace进行替换:

def replace_str(credit_code: str):
    credit_code = credit_code.replace('O', '0')
    credit_code = credit_code.replace('o', '0')
    credit_code = credit_code.replace('l', '1')
    credit_code = credit_code.replace('?', 'T')
    credit_code = credit_code.replace('I', '1')
    credit_code = credit_code.replace('x', 'X')
    credit_code = credit_code.upper()
    return credit_code

五、完整代码

改进后的完整版代码如下:

import os.path
import re
import shutil
import time
import PyPDF2
from paddleocr import PaddleOCR

# 需要的目录
dirs = ['finished', 'pdf', 'pdf_crop']
for d in dirs:
    if not os.path.exists(f'./{d}'):
        os.makedirs(f'./{d}')

# 需要的路径
PDF_PATH = './pdf'
SAVE_PATH = './finished'
CROP_PATH = './pdf_crop'


# 定义用于裁剪的函数
def split(page, tup):
    page.mediaBox.lowerLeft = (tup[0], tup[1])
    page.mediaBox.lowerRight = (tup[2], tup[1])
    page.mediaBox.upperLeft = (tup[0], tup[3])
    page.mediaBox.upperRight = (tup[2], tup[3])


# 定义替换规则
def replace_str(credit_code: str):
    credit_code = credit_code.replace('O', '0')
    credit_code = credit_code.replace('o', '0')
    credit_code = credit_code.replace('l', '1')
    credit_code = credit_code.replace('?', 'T')
    credit_code = credit_code.replace('I', '1')
    credit_code = credit_code.replace('x', 'X')
    credit_code = credit_code.upper()
    return credit_code


st1 = time.perf_counter()

for file_name in os.listdir(PDF_PATH):  # 返回pdf目录下的文件夹名称
    pdf_names = os.listdir(rf'{PDF_PATH}/{file_name}')  # 包含pdf所有文件名组成的列表
    pdf_dir = os.path.join(PDF_PATH, file_name)  # pdf路径名 + 下面的文件名
    finished_path = os.path.join(SAVE_PATH, file_name)  # 完成时存储的路径,finished + pdf文件夹名

    for pdf_name in pdf_names:
        pdf_name_path = rf'{pdf_dir}/{pdf_name}'  # pdf文件的具体路径,包含pdf文件本身

        # 打开与关闭操作
        output_file = PyPDF2.PdfFileWriter()
        inputStream = open(pdf_name_path, 'rb')
        input_file = PyPDF2.PdfFileReader(inputStream)

        # pdf处理操作
        this_page = input_file.getPage(0)  # 获取第1页
        split(this_page, (60, 700, 500, 800))
        output_file.addPage(this_page)
        pdf_crop_save = rf'{CROP_PATH}/{pdf_name}'  # pdf_crop + pdf文件名
        outputStream = open(pdf_crop_save, "wb")
        output_file.write(outputStream)  # 将切割的文件存储在pdf_crop文件下

        # 关闭pdf文件,否则会报文件被占用的错
        inputStream.close()
        outputStream.close()

        # 调用PaddleOCR进行识别
        OCR = PaddleOCR(use_angle_cls=True, lang='en')
        result_ocr = OCR.ocr(pdf_crop_save, cls=True)
        credit_code = re.findall('[0-9A-Z]{18}', str(result_ocr))

        # 判断结果
        if credit_code:
            credit_code = credit_code[0]
            credit_code = replace_str(credit_code)

            if not os.path.exists(f'./finished/{file_name}'):
                os.makedirs(f'./finished/{file_name}')

            if os.path.exists(rf'{finished_path}/{credit_code}.pdf'):
                print(f'文件名称存在重复,{credit_code}')
                continue

            shutil.move(rf'{pdf_dir}/{pdf_name}', rf'{finished_path}/{credit_code}.pdf')
            print('完成文件移动:', credit_code)
            st2 = time.perf_counter()
            print('累计耗时:', st2 - st1)

        else:
            print('识别失败:', credit_code, '累计耗时:', time.perf_counter() - st1)

大家有更好的方法,欢迎留言讨论,也欢迎大家指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值