信息抽取任务旨在从非结构化的自然语言文本中提取结构化信息。在本系列项目中,将讨论如何又好又快地实现一个简历信息提取任务。
作为该系列文章的第一篇,我们将首先从数据处理着手,探讨Word、PDF格式文档信息提取的一些基本方法。
本文使用的简历数据集是脱敏之后的中文人才简历数据和标注数据。
标注类别包括:姓名、出生年月、性别、电话、最高学历、籍贯、落户市县、政治面貌、毕业院校、工作单位、工作内容、职务、项目名称、项目责任、学位、毕业时间、工作时间、项目时间共18个字段。
在训练数据集中,每个“毕业院校、学位、毕业时间”为一组,以“教育经历”列表给出;每个“工作单位、工作内容、职务、工作时间”为一组,以“工作经历”列表给出;每个“项目名称、项目责任、项目时间”为一组,以“项目经历”列表给出。
训练数据同时给出了PDF和Word格式。
1 word文档信息提取.docx
格式现在.docx
格式是我们最常见到的一种Word文档格式了,它是微软采用类XML格式标准定义的Word文件。
正因如此,相比早期的.doc
文件,.docx
文件的兼容性大幅提升。
那么,.docx
文件又是如何封装的?.docx
实际上是一个zip的压缩文件,比如我们任选一个.docx
文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDww3qlB-1636944651045)(https://ai-studio-static-online.cdn.bcebos.com/acc7880fa91247a5a2e3fa8e7c5d3670aacca1de565f4c84ad3eb59072898d34)]
再把它的文件名后缀直接修改为.zip
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nb8vRFMz-1636944651047)(https://ai-studio-static-online.cdn.bcebos.com/14181f975e9848699ec46d5462ea9b87c12ed7c9ccb74022a865ab353d6b25f9)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG7tYJCb-1636944651048)(https://ai-studio-static-online.cdn.bcebos.com/ae2c0b4c43074dab8f07bda0e0ec7d596809c919ef6f4fb6bae0f83484460c22)]
打开这个压缩包,我们会看到,其实文档的正文,是以XML格式表示的,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3PlibKu-1636944651049)(https://ai-studio-static-online.cdn.bcebos.com/2ad3cac751834ffc987521534a5f05c07b3866a1f7f9460a8db4367ccea01e13)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m00tpBaH-1636944651050)(https://ai-studio-static-online.cdn.bcebos.com/000a892628ac40dba8aeb042b5e6d951772bd02d00c74c2abf630b42dfab9f03)]
因此,其实除了python-docx
库之外,BeautifulSoup
也可以用来提取Word文档信息。
python-docx
提取文档信息python-docx
其实在项目PaddleHub机器翻译:文档的批量翻译中提到过
这里不做过多赘述,我们直奔主题,选取一份word简历文档,尝试提取信息。
# 解压缩数据集
!unzip data/data40148/train_20200121.zip
# 安装依赖库
!pip install python-docx
from docx import Document
from docx.shared import Inches
def get_paragraphs_text(path):"""获取所有段落的文本:param path: word路径:return: list类型,如:['Test', 'hello world', ...]"""document = Document(path) # 有的简历是表格式样的,因此,不仅需要提取正文,还要提取表格col_keys = [] # 获取列名col_values = [] # 获取列值index_num = 0# 表格提取中,需要添加一个去重机制fore_str = ""cell_text = ""for table in document.tables:for row_index,row in enumerate(table.rows):for col_index,cell in enumerate(row.cells):if fore_str != cell.text:if index_num % 2==0:col_keys.append(cell.text)else:col_values.append(cell.text)fore_str = cell.textindex_num +=1cell_text += cell.text + '\n'# 提取正文文本paragraphs_text = ""for paragraph in document.paragraphs:# 拼接一个list,包括段落的结构和内容paragraphs_text += paragraph.text + "\n"return cell_text, paragraphs_text
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IjR1FCv3-1636944651051)(https://ai-studio-static-online.cdn.bcebos.com/d65ae7bb5eab45e0901df04f08837eb21e0968a120f6402cac257da88b8d99aa)]
# 提取文档信息,这是一个表格式简历
cell_text, paragraphs_text = get_paragraphs_text('resume_train_20200121/docx/0043e770a330.docx')
print(cell_text, paragraphs_text)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9GJ9ghq-1636944651051)(https://ai-studio-static-online.cdn.bcebos.com/c0cc78f2688c4f9ab4f9934ea2584b946826aafa4d914d8a8246882e94a71f1c)]
# 提取文档信息,这是一个表格正文混搭式简历(会出现一些混乱的情况,因为在这个文件里,标题是以表格形式存在)
cell_text, paragraphs_text = get_paragraphs_text('resume_train_20200121/docx/0052b7958e89.docx')
print(cell_text, paragraphs_text)
杜素宁
MOBILE : 15904130130
E-MAIL:0da08x@163.com
Address:云南省昭通市个人信息民族:汉
籍贯:云南省昭通市
性别:女 年龄: 18
教育经历2008.08-2012.08
北方工业大学
食品科学与工程
学士学位
主要经历
Project Experience1997.06-2010.07
江苏华英企业管理股份有限公司
水处理工程师
1991年12月-2012年09月
和宇健康科技股份有限公司
市场营销专员
2007/05-2010/03
深圳市有棵树科技有限公司
拼多多运营
个人技能
Personal Skills个人荣誉
Personal Honor工作经历:
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作工作经历:
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象。工作经历:
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析,产品适用性分析;3.按公司规定完成SalesPipeline信息记录吃饭
优秀学生干部
如果说这种方法有什么缺点的话,就是遇到一些艺术字等标题,与后面的文字从语义上是连续的,但是XML格式不连续。
# 安装依赖库
!pip install bs4
from zipfile import ZipFile
from bs4 import BeautifulSoupdocument = ZipFile('resume_train_20200121/docx/0052b7958e89.docx')
xml = document.read("word/document.xml")
wordObj = BeautifulSoup(xml.decode("utf-8"))
texts = wordObj.findAll("w:t")
for text in texts:print(text.text)
杜素宁
MOBILE
:
15904130130
E-MAIL
:
0da08x@163.com
A
ddress
:云南省昭通市
个人信息
民族:
汉
籍贯:
云南省昭通市
性别:
女年龄
: 18教育经历
2008.08-2012.08北方工业大学
食品科学与工程
学士学位主要经历
P
roject Experience
工作经历:
1997.06-2010.07
江苏华英企业管理股份有限公司
水处理工程师
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作工作经历:
1991年12月-2012年09月
和宇健康科技股份有限公司
市场营销专员
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象。工作经历:
2007/05-2010/03
深圳市有棵树科技有限公司
拼多多运营
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析,产品适用性分析;3.按公司规定完成SalesPipeline信息记录个人技能
PersonalSkills
吃饭个人荣誉
P
ersonal Honor
优秀学生干部
2 使用pdfplumber库提取PDF信息
!pip install pdfplumber
import pdfplumber
import pandas as pdwith pdfplumber.open("resume_train_20200121/pdf/0052b7958e89.pdf") as pdf:page = pdf.pages[0] # 第一页的信息text = page.extract_text()print(text)
杜素宁
MOBILE : 15904130130
E-MAIL:0da08x@163.com Address:云南省昭通市
个人信息 民族:汉 籍贯:云南省昭通市 性别:女 年龄: 18 教育经历 2008.08-2012.08 北方工业大学 食品科学与工程 学士学位 主要经历
Project Experience
工作经历:
1997.06-2010.07 江苏华英企业管理股份有限公司 水处理工程师
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销
售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作 工作经历:
1991年12月-2012年 和宇健康科技股份有限公司 市场营销专员
09月
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和
收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象。 工作经历:
2007/05-2010/03 深圳市有棵树科技有限公司 拼多多运营
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析,
产品适用性分析;3.按公司规定完成SalesPipeline信息记录
with pdfplumber.open("resume_train_20200121/pdf/0052b7958e89.pdf") as pdf:page = pdf.pages[0] # 第一页的信息table = page.extract_tables()for t in table:# 得到的table是嵌套list类型,转化成DataFrame更加方便查看和分析df = pd.DataFrame(t[1:], columns=t[0])print(t)
[['个人信息', None, None, '', None, None, None], ['民族:汉', '', '籍贯:云南省昭通市', None, '', '性别:女', '年龄: 18']]
[['教育经历', None, None, '', None, None], ['2008.08-2012.08', '', '北方工业大学', None, '食品科学与工程', '学士学位']]
[['主要经历 \nProject Experience', '']]
[['1997.06-2010.07', '江苏华英企业管理股份有限公司', '水处理工程师']]
[['', '1991年12月-2012年', '', '和宇健康科技股份有限公司', '市场营销专员'], [None, '09月', None, None, None]]
[['2007/05-2010/03', '深圳市有棵树科技有限公司', '拼多多运营']]
因此,对于同时有表格(标题用表格表示)和正文交错的文档,pdfplumber也能很好地识别,如果是布局标准的纵向PDF,效果还是非常稳定的。
3 通过PPOCR识别PDF这个方式看起来有点绕,如果可以直接识别PDF和word,为什么还要转图片用OCR呢?
不过,在很多时候,它还是一个很必要的补充手段,毕竟,如果简历的PDF是那种扫描式的,甚至word文档万一就是个图片……
手段多一些,总是有备无患的。
在本文中,我们尝试的是PP-Structure工具包,它提供了PDF图片表格一键提取解决方案。
# 安装依赖库
# pywt可能要重启内核
!pip install pywt -i https://mirror.baidu.com/pypi/simple
!pip install "paddleocr>=2.2" --no-deps -r requirements.txt
# 安装依赖库
!pip install -U https://paddleocr.bj.bcebos.com/whl/layoutparser-0.0.0-py3-none-any.whl
!pip install PyMuPDF
import datetime
import os
import fitz # fitz就是pip install PyMuPDF
import cv2
import shutil
from paddleocr import PPStructure,draw_structure_result,save_structure_res
import numpy as np
def pyMuPDF_fitz(pdfPath, imagePath):startTime_pdf2img = datetime.datetime.now() # 开始时间print("imagePath=" + imagePath)pdfDoc = fitz.open(pdfPath)for pg in range(pdfDoc.pageCount):page = pdfDoc[pg]rotate = int(0)# 每个尺寸的缩放系数为4,这将为我们生成分辨率提高4的图像。# 此处若是不做设置,默认图片大小为:792X612, dpi=96zoom_x = 4 # (1.33333333-->1056x816) (2-->1584x1224)zoom_y = 4mat = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)pix = page.getPixmap(matrix=mat, alpha=False)if not os.path.exists(imagePath): # 判断存放图片的文件夹是否存在os.makedirs(imagePath) # 若图片文件夹不存在就创建pix.writePNG(imagePath + '/' + 'images_%s.png' % pg) # 将图片写入指定的文件夹内endTime_pdf2img = datetime.datetime.now() # 结束时间print('pdf转图片时间=', (endTime_pdf2img - startTime_pdf2img).seconds)
pdfDir = './resume_train_20200121/pdf'def gci(filepath):
#遍历filepath下所有文件 files = os.listdir(filepath) for fi in files:# 将转换的图片保存到对应imgs的对应子目录下 pyMuPDF_fitz(os.path.join(filepath,fi), os.path.join('imgs',fi[:-4]))
# 遍历PDF文件并转换图片。如果读者觉得转换时间比较长,可以提前中止cell。
gci(pdfDir)
下面这两张简历我们可以看出,对于一些表格式简历,版面分析会将其判定为表格。
其它的简历,可能被认为版面上是图片甚至既没有图片也没有表格。
如果使用PP-Structure,对这几种检测结果,需要分别进行后处理。
import cv2
import layoutparser as lp
image = cv2.imread('imgs/0043e770a330/images_0.png')
image = image[..., ::-1]# 加载模型
model = lp.PaddleDetectionLayoutModel(config_path="lp://PubLayNet/ppyolov2_r50vd_dcn_365e_publaynet/config",threshold=0.5,label_map={0: "Text", 1: "Title", 2: "List", 3:"Table", 4:"Figure"},enforce_cpu=False,enable_mkldnn=True)
# 检测
layout = model.detect(image)# 显示结果
show_img = lp.draw_box(image, layout, box_width=3, show_element_type=True)
show_img
from bs4 import BeautifulSoup
import re
# 表格类的预测结果已经封装在HTML标签中了
table_engine = PPStructure(show_log=True)
img = cv2.imread('imgs/0043e770a330/images_0.png')
result = table_engine(img)
result[0]['res']
# 折行问题需要在后续进一步解决
html = BeautifulSoup(result[0]['res'],'html.parser', from_encoding='utf-8')
list(html.strings)
['姓名','郝淑宁','性别','男','出生日期','2000.04','民族','汉族','联系电话','13602173036','籍贯','黑龙江省双鸭山「邮箱 市','bhluo@live.com','教育背景','毕业时间:','2012.08 - 2016.08','毕业学校:北京政法职业学院','学历/学位:大学本科/学士学位','专业:动物生产','工作经验','时间:1991年07月-2016年12月','部门:研发部','公司:江苏达科信息科技有限公司','职位:渠道商务','1、申请票据,购***,准备和报送会计报表,会报税及报税流程;2、现金及银行收付处理,制作记帐','凭证,银行对帐,单据审核,开具与保管发票;3、协助财会文件的准备、归档和保管;4、固定资产和','工作;','低值易耗品的登记和管理;5、负责与银行、税务等部门的对外联络;6、协助领导完成其他日常事务性','时间:1992年05月-2015年05月','公司:盛趣信息技术有限公司部门:研发部','职位:商场空调工5.5k','1、根据公司制定的目标,制定有效销售计划,开发客户,完成销售任务;2、关注市场变化,收集有效','的市场信息,为公司的销售策略作参考资料依据;3、做好客户管理工作,根据不同客户的需求特点和 相关的信息、数据,提供解决方案;4、统筹客户维系工作,做好客户拜访计划,并按计划进行拜访,','推进项目;5、协助领导做好公司其他工作。','时间:2006.06-2018.06','公司:广州市华粤行仪器有限公司','部门:研发部','职位:Golang开发工程师','1、登记收集资料,整理文件表格;2、辅助就业指导老师为鹏程学员推荐工作;3、发布招聘信息,与','鹏程学员互动;4、对接好企业,为鹏程学员推荐心仪工作到面到岗服务。','项目经验','2001.12-2012.01 1、协助制定市场活动计划,组织落实市场活动;2、负责展会活动策划与相关活动支持,市场推广资料','珠三角最低工资标准的执行、影响与对策研究']
import cv2
import layoutparser as lp
image = cv2.imread('imgs/0052b7958e89/images_0.png')
image = image[..., ::-1]# 加载模型
model = lp.PaddleDetectionLayoutModel(config_path="lp://PubLayNet/ppyolov2_r50vd_dcn_365e_publaynet/config",threshold=0.5,label_map={0: "Text", 1: "Title", 2: "List", 3:"Table", 4:"Figure"},enforce_cpu=False,enable_mkldnn=True)
# 检测
layout = model.detect(image)# 显示结果
show_img = lp.draw_box(image, layout, box_width=3, show_element_type=True)
show_img
with open(os.path.join('data', 'res.txt'), 'w', encoding='utf8') as f:for img in os.listdir('imgs/0043e770a330'):img_path = os.path.join(fi_d,img)img = cv2.imread(img_path)result = table_engine(img)print(result[0]['res'])for box, rec_res in zip(result[0]['res'][0], result[0]['res'][1]):f.write('{}\t{}\n'.format(np.array(box).reshape(-1).tolist(), rec_res))
table_engine = PPStructure(show_log=True)
img = cv2.imread('imgs/0052b7958e89/images_0.png')
result = table_engine(img)
result[0]['res']
for box, rec_res in zip(result[0]['res'][0], result[0]['res'][1]):print(rec_res[0])
杜素宁
MOBILE:15904130130
E-MAIL:0da08x@163.com
Address:云南省昭通市
个人信息
性别:女
民族:汉
籍贯:云南省昭通市
年龄:18
教育经历
北方工业大学
食品科学与工程
2008.08-2012.08
学士学位
主要经历
Project Experience
工作经历:
1997.06-2010.07
江苏华英企业管理股份有限公司
水处理工程师
工作内容:
1.负责部门内日常用品的采购;2.做好与公司内其他部门的对接工作;3.协助部门进行办公环境管理和后勤管理工作;4.销
售人员与公司的信息交流,随时保持与市场销售人员的电话沟通,销售政策及公司文件的及时传达。5.领导交办的其他工作
工作经历:
1991年12月-2012年
和宇健康科技股份有限公司
市场营销专员
09月
工作内容:
1、做好消费宾客的迎、送接待工作,接受宾客各种渠道的预定并加以落实;2、礼貌用语,详细做好预订记录;3、了解和
收集宾客的建议和意见并及时反馈给上级领导;4、以规范的服务礼节,树立公司品牌优质,文雅的服务形象
工作经历:
2007/05-2010/03
深圳市有棵树科技有限公司
拼多多运营
工作内容:
1.负责规定区域的产品销售,做好产品介绍,确认订单,回款等销售相关工作;2.做好客户背景资料调查,竞争对手分析
产品适用性分析;3.按公司规定完成SalesPipeline信息记录
4 小结
在本文中,我们探索并比较了Word、PDF、图片格式的简历文件信息提取方法,在后续项目中,将结合PaddleNLP的信息提取技术,尝试将非结构化的简历文件,提取成标注文件指定的格式,并验证性能。