即将进行NLP项目,边学边做。
第一步是将大篇的文本数据处理成结构化的数据。
1. 读取PDF和docx文件
1.1. 读取PDF
原理与代码参考:https://blog.csdn.net/xc_zhou/article/details/81009809
python中读取pdf的库挺多,各有各的优缺点。本文就介绍目前较好的两种pdfminer 和pdfplumber 的使用
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
|