分享

python读取pdf和docx

 wenxuefeng360 2022-07-02 发布于四川

即将进行NLP项目,边学边做。
第一步是将大篇的文本数据处理成结构化的数据。

1. 读取PDF和docx文件

1.1. 读取PDF

原理与代码参考:https://blog.csdn.net/xc_zhou/article/details/81009809
python中读取pdf的库挺多,各有各的优缺点。本文就介绍目前较好的两种pdfminerpdfplumber的使用

pdfminer

首先要安装以下几个库

pip install pdfminer
pip install pdfminer3k
pip install pdfminer.six

我只安装了第一个库就能成功运行了,如果有问题可以尝试把下面两行也安上。

将一个文件夹下所有PDF文件都转为txt的程序:

# -*- coding:utf-8 -*-
# 来源:https://blog.csdn.net/xc_zhou/article/details/81009809
import os,re
from pdfminer.pdfinterp import PDFResourceManager,PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams

'''
pip install pdfminer3k
pip install pdfminer.six 安装这个引入的内容不会报错
'''

#将一个pdf转换成txt
def pdfTotxt(filepath,outpath):
    try:
        fp = open(filepath, 'rb')
        outfp=open(outpath,'w')
        #创建一个PDF资源管理器对象来存储共享资源,caching = False不缓存
        rsrcmgr = PDFResourceManager(caching = False)
        # 创建一个PDF设备对象
        laparams = LAParams()
        device = TextConverter(rsrcmgr, outfp, codec='utf-8', laparams=laparams,imagewriter=None)
        #创建一个PDF解析器对象
        interpreter = PDFPageInterpreter(rsrcmgr, device)
        for page in PDFPage.get_pages(fp, pagenos = set(),maxpages=0,
                                      password='',caching=False, check_extractable=True):
            page.rotate = page.rotate % 360
            interpreter.process_page(page)
        #关闭输入流
        fp.close()
        #关闭输出流
        device.close()
        outfp.flush()
        outfp.close()
    except Exception as e:
         print("Exception:%s",e)

#一个文件夹下的所有pdf文档转换成txt
def fileTotxt(fileDir):
    files=os.listdir(fileDir)
    tarDir=fileDir+'txt'
    if not os.path.exists(tarDir):
        os.mkdir(tarDir)
    replace=re.compile(r'\.pdf',re.I)
    for file in files:
        filePath=fileDir+'\\'+file
        outPath=tarDir+'\\'+re.sub(replace,'',file)+'.txt'
        pdfTotxt(filePath,outPath)
        print("Saved "+outPath)


if __name__ == '__main__':
    # pdfTotxt('000.pdf', 'test.txt')
    fileTotxt('./data/pdf/')

折叠

结果:基本的文本信息都能够很好的读取,但有的部分不能很好的读取,如:

原文 txt
(数据来源于网络:华安宝利配置证券投资基金基金合同)
可以看到句尾换行后,一部分句尾的内容会集中到其他地方(目前还没了解是什么原因……),读取表格的效果也不佳。
并且,转换的部分主要在pdfTotxt()函数中,可以看到pdfminer构造了pdf资源管理器对象、pdf设备对象、pdf解析器对象来进行转换,过程十分繁琐。
于是后面发现了一个读取PDF更好的库:pdfplumber

pdfplumber

先上程序:

import pdfplumber

PDF_PATH = './data/pdf/'
FILE_NAME = '华安宝利配置证券投资基金基金合同'

path = PDF_PATH + FILE_NAME + '.pdf'
pdf = pdfplumber.open(path)
f = open(PDF_PATH + FILE_NAME + '.txt', 'w')

for page in pdf.pages:
    # 获取当前页面的全部文本信息,包括表格中的文字
    str = page.extract_text()
    print(str)
    f.write(str + '\n')

pdf.close()
f.close()

结果:不会出现如上的明显错误:

原文 txt
pdfplumber使用方式简单,读取方式为按页读取,后续操作也易于理解和修改。并且读取表格效果较好,有专门的提取表格的函数extract_tables()
程序:
for page in pdf.pages:
    for table in page.extract_tables():
        # print(table)
        for row in table:
            print(row)
        print('---------- 分割线 ----------')

结果:

原文 输出结果
表格内容基本读取,并且以list形式存储。但其中顺序不是很对,“最低比例”这列也没有读取到,可能读取结果因表格的制作而异。

总结

根据效果,选择使用pdfplumber读取pdf。
但读取pdf普遍存在一些问题:
(1)会根据PDF的显示,把相应的换行符也读入进去,但是一句话并没有结束。因此会与自然分段的换行符混淆,影响之后将文本一条条提取出来。
(2)会有空行和页码。
这些问题会在之后解决

1.2. 读取Docx文件

读取Docx文件主要使用docx库,安装:pip install python-docx
程序:

from docx import Document

DOC_PATH = './data/docx/'
FILE_NAME = '文件名'
document = Document(DOC_PATH + FILE_NAME + '.docx')
f = open(DOC_PATH + FILE_NAME + '.txt', 'w')
i = 0
for paragraph in document.paragraphs:
    f.write(paragraph.text + '\n')
    print(i, paragraph.text)
    i += 1
f.close()

由于docx文件与pdf文件的区别,读取docx文件比较轻松,且可编辑性很好。
结果:

可以看到读取docx文件是按行读取的,并且其“一行”是根据docx文件中的换行符定义的,即一句完整的句子或一个自然段才算作一行。

2. 处理PDF和docx文件

2.1. 处理PDF文件

目前仍在解决换行的问题。

2.2. 处理docx文件

docx文件读取已基本将文本内容一段段分开,此次NLP项目为分类某一项(例:1、2、3、)的内容属性,因此要将整个文本分成一条条。
关键思想为:根据一条的数字编号对其进行编码。
以某一节的内容为例,最小的分类标题为三级标题,即(一)1.(1),那么其对应的编码为:111。之后112、113……以此类推。
判断标题编号的程序,主要通过标题前面的标号实现:

COL1NUM = ['(一', '(二', '(三', '(四', '(五', '(六', '(七', '(八', '(九', '(十']
COL2NUM = ['1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '0.']
COL3NUM = ['(1', '(2', '(3', '(4', '(5', '(6', '(7', '(8', '(9']


def sepNum(list):
    # (一)1.(1)。(①②③不划分)
    col_num = [0, 0, 0]
    newlist = []
    title_flag = 0
    for line in list:
        for index in range(len(COL1NUM)):
            if line.find(COL1NUM[index]) == 0:
                col_num[0] += 1
                col_num[1] = 0
                col_num[2] = 0
                title_flag = 1
        for index in range(len(COL2NUM)):
            if 0 <= line.find(COL2NUM[index]) <= 2:  # <=2时最多到三位数:111.
                col_num[1] += 1
                col_num[2] = 0
        for index in range(len(COL3NUM)):
            if line.find(COL3NUM[index]) == 0:
                col_num[2] += 1
        if title_flag == 1 or line == '':
            title_flag = 0  # 是一级标题的话,跳过;或是空行
        else:
            newlist.append([col_num[0], col_num[1], col_num[2], line])
    print(col_num)
    return newlist

其中一级标题:“(一)”,基本为纯标题,没有实际意义,已去掉。得到结果保存为CSV文件,如下所示:

3. 更多

完整程序放在Github中:https://github.com/AionWU/pythonReadfile
目前程序仍在推进中。

内容如有误或有更好的方法希望大佬们给予批评指正 Orz

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多