在人力资源招聘场景中,HR每天需要处理大量简历,人工录入信息效率低、易出错。本案例将通过Python结合昇思框架,实现简历自动读取、关键信息智能抽取、技能标签标准化、结构化输出 全流程,代码可直接运行、可用于毕业设计与课程实战。
本项目全程基于华为昇思MindSpore 国产AI框架,真正实现国产化AI技术在智能招聘领域的应用。
一、项目功能与技术栈
1. 核心功能
支持 .docx / .pdf 双格式简历文本提取
自动识别抽取:姓名、电话、邮箱、学历、学校、专业、技能
技能标签标准化(如MindSpore/昇思统一、大模型/深度学习统一)
输出标准JSON结构化数据
2. 技术栈
深度学习框架:昇思MindSpore
文档处理:python-docx、pdfplumber
文本处理:正则表达式、jieba分词
数据处理:pandas、json
二、环境配置与完整代码实现
1. 安装所需依赖包,执行以下命令:
!pip install PyPDF2 python-docx pdfplumber pandas jieba
这个安装过程有点长,请耐心等待,安装完成如下图所示:
依赖说明:
PyPDF2:辅助解析PDF文件,提升PDF文本提取兼容性
python-docx:读取Word(.docx)文件,提取简历文本内容
pdfplumber:核心PDF解析工具,精准提取PDF每页文本,避免乱码
pandas:数据处理工具,预留后续简历数据批量处理、入库功能
jieba:中文分词工具,为后续文本向量化、关键词优化提供支持
注:昇思MindSpore已安装,此处无需重复安装。
2. 完整可运行代码
import re
import json
import jieba
import pandas as pd
import mindspore as ms
from docx import Document
import pdfplumber
# ====================== 昇思环境配置 ======================
ms.set_context(mode=ms.GRAPH_MODE, device_target="CPU")
# ====================== 1. PDF / Word 文档解析 ======================
def extract_word_text(file_path):
try:
doc = Document(file_path)
return "\n".join([para.text.strip() for para in doc.paragraphs if para.text.strip()])
except:
return ""
def extract_pdf_text(file_path):
text = ""
try:
with pdfplumber.open(file_path) as pdf:
for page in pdf.pages:
t = page.extract_text()
if t:
text += t + "\n"
except:
pass
return text
def extract_resume(file_path):
if file_path.endswith(".docx"):
return extract_word_text(file_path)
elif file_path.endswith(".pdf"):
return extract_pdf_text(file_path)
else:
return ""
# ====================== 2. 简历 NER 实体抽取 ======================
class ResumeNER:
def __init__(self):
self.phone_pattern = re.compile(r'1[3-9]\d{9}')
self.email_pattern = re.compile(r'\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*')
self.edu_list = ["本科", "硕士", "博士", "大专"]
self.school_keywords = ["大学", "学院", "理工", "师范", "科技"]
self.major_list = ["计算机", "软件", "电子", "自动化", "人工智能"]
self.skill_base = [
"Python", "Java", "MindSpore", "昇思", "MySQL", "Redis",
"Linux", "大模型", "深度学习", "Docker", "Git"
]
def extract(self, text):
res = {}
lines = text.split("\n")
# 电话
phones = self.phone_pattern.findall(text)
res["phone"] = phones[0] if phones else ""
# 邮箱
emails = self.email_pattern.findall(text)
res["email"] = emails[0] if emails else ""
# 学历
res["education"] = ""
for edu in self.edu_list:
if edu in text:
res["education"] = edu
break
# 学校
res["school"] = ""
for line in lines:
if any(k in line for k in self.school_keywords):
res["school"] = line.strip()
break
# 专业
res["major"] = ""
for m in self.major_list:
if m in text:
res["major"] = m
break
# 技能
skills = []
for s in self.skill_base:
if s in text:
skills.append(s)
res["skills"] = skills
# 姓名
res["name"] = ""
for line in lines[:5]:
line = line.strip()
if 2 <= len(line) <= 4 and all('\u4e00' <= c <= '\u9fff' for c in line):
res["name"] = line
break
return res
# ====================== 3. 技能标准化 ======================
SKILL_MAP = {
"python": ["Python", "python", "py"],
"java": ["Java", "java"],
"mindspore": ["MindSpore", "昇思"],
"mysql": ["MySQL", "mysql"],
"大模型": ["大模型", "深度学习", "机器学习"]
}
def normalize_skills(skill_list):
std = []
for s in skill_list:
for standard, alias in SKILL_MAP.items():
if s in alias:
std.append(standard)
break
else:
std.append(s)
return list(set(std))
# ====================== 4. 全流程主函数 ======================
def resume_parse_pipeline(file_path=None):
# 无文件则使用内置模拟简历(直接运行)
if file_path is None:
demo = """
李四
电话:12222222222
邮箱:lisi@qianduanjidi.com
教育经历:
社会大学 本科 计算机科学与技术
专业技能:
Python、MindSpore、MySQL、Redis、Linux、大模型、深度学习
"""
text = demo
else:
text = extract_resume(file_path)
print("=" * 50)
print("📄 提取文本成功")
print("=" * 50)
# NER 抽取
ner = ResumeNER()
result = ner.extract(text)
# 技能标准化
result["skills_standard"] = normalize_skills(result["skills"])
# 输出 JSON
output = {
"code": 0,
"msg": "解析成功",
"data": result
}
print("\n✅ 结构化结果:\n")
print(json.dumps(output, ensure_ascii=False, indent=2))
return output
# ====================== 直接运行 ======================
if __name__ == "__main__":
# resume_parse_pipeline() # 无文件测试
resume_parse_pipeline("简历.docx") # 有文件用这个
3. 简历.docx
三、代码核心模块讲解
以下结合代码逻辑,逐模块拆解核心功能,理解每一行代码的作用,适配昇思智能招聘实战场景,方便后续修改和扩展。
1 . 代码逐模块详细解析
import re
import json
import jieba
import pandas as pd
import mindspore as ms
from docx import Document
import pdfplumber
(1)导入依赖包与昇思环境配置
re:正则表达式工具,核心用于抽取电话、邮箱(最精准的文本匹配方式);
这部分是代码的“基础准备”,导入所有需要用到的工具,每一个包都对应核心功能,避免冗余:
jieba:中文分词工具,预留后续文本优化(如岗位匹配时的分词处理);
json:用于将解析结果封装成JSON格式,方便后续对接招聘系统;
mindspore as ms:导入昇思框架,挂载AI计算环境,是本项目“国产化AI”的核心标识;
pandas:数据处理工具,预留批量解析简历、数据入库功能;
pdfplumber:读取PDF简历的核心工具,比普通PDF解析工具更精准,避免乱码。
Document(from docx):读取Word简历的核心工具;
# ====================== 昇思环境配置 ======================
ms.set_context(mode=ms.GRAPH_MODE, device_target="CPU")
•device_target=“CPU”:指定使用CPU运行,适配大多数设备(新手无需配置GPU,后续可切换为昇腾芯片/GPU,贴合国产化部署)。
•mode=ms.GRAPH_MODE:启用昇思计算图模式,提升代码运行效率,为后续接入昇思大模型做准备;
(2)PDF/Word文档解析模块(3个核心函数)
# ====================== 1. PDF / Word 文档解析 ======================
def extract_word_text(file_path):
try:
doc = Document(file_path)
return "\n".join([para.text.strip() for para in doc.paragraphs if para.text.strip()])
except:
return ""
def extract_pdf_text(file_path):
text = ""
try:
with pdfplumber.open(file_path) as pdf:
for page in pdf.pages:
t = page.extract_text()
if t:
text += t + "\n"
except:
pass
return text
def extract_resume(file_path):
if file_path.endswith(".docx"):
return extract_word_text(file_path)
elif file_path.endswith(".pdf"):
return extract_pdf_text(file_path)
else:
return ""
读取Word(.docx)简历:
•[para.text.strip() for para in doc.paragraphs]:遍历Word所有段落,提取每段文本并去除前后空白;
•Document(file_path):打开指定路径的Word文件;
•try…except:异常捕获,防止Word文件损坏、路径错误导致程序崩溃,出错时返回空文本。
•if para.text.strip():过滤空段落(避免简历中的空白行干扰后续识别);
读取PDF简历:
•for page in pdf.pages:遍历PDF每一页,逐页提取文本;
•pdfplumber.open(file_path):打开指定路径的PDF文件,with语句自动关闭文件,避免资源泄露;
1.text += t + “\n”:将每页文本拼接,保留页面分隔逻辑,便于后续按行识别信息(如姓名、学校)。
2.if t:过滤空白页(部分PDF空白页无文本,避免无效内容);
统一入口,自动识别文件格式:
file_path.endswith(“.pdf”):判断文件是否为PDF格式,是则调用extract_pdf_text函数;
file_path.endswith(“.docx”):判断文件是否为Word格式,是则调用extract_word_text函数;
(3)简历实体抽取(NER)模块(ResumeNER类)
# ====================== 2. 简历 NER 实体抽取 ======================
class ResumeNER:
def __init__(self):
self.phone_pattern = re.compile(r'1[3-9]\d{9}')
self.email_pattern = re.compile(r'\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*')
self.edu_list = ["本科", "硕士", "博士", "大专"]
self.school_keywords = ["大学", "学院", "理工", "师范", "科技"]
self.major_list = ["计算机", "软件", "电子", "自动化", "人工智能"]
self.skill_base = [
"Python", "Java", "MindSpore", "昇思", "MySQL", "Redis",
"Linux", "大模型", "深度学习", "Docker", "Git"
]
def extract(self, text):
res = {}
lines = text.split("\n")
# 电话
phones = self.phone_pattern.findall(text)
res["phone"] = phones[0] if phones else ""
# 邮箱
emails = self.email_pattern.findall(text)
res["email"] = emails[0] if emails else ""
# 学历
res["education"] = ""
for edu in self.edu_list:
if edu in text:
res["education"] = edu
break
# 学校
res["school"] = ""
for line in lines:
if any(k in line for k in self.school_keywords):
res["school"] = line.strip()
break
# 专业
res["major"] = ""
for m in self.major_list:
if m in text:
res["major"] = m
break
# 技能
skills = []
for s in self.skill_base:
if s in text:
skills.append(s)
res["skills"] = skills
# 姓名
res["name"] = ""
for line in lines[:5]:
line = line.strip()
if 2 <= len(line) <= 4 and all('\u4e00' <= c <= '\u9fff' for c in line):
res["name"] = line
break
return res
这是代码的“核心大脑”,负责从提取的纯文本中,自动识别并抽取HR最关心的7类信息(姓名、电话、邮箱、学历、学校、专业、技能),采用“正则+关键词匹配”模式,轻量易运行,新手可快速理解,后续可替换为昇思NER大模型提升精度。
self.phone_pattern:正则匹配11位手机号(13-9开头,覆盖所有合法手机号格式);
__init__方法:初始化匹配规则和关键词库,提前定义好需要识别的内容,避免重复代码:
关键词库:提前定义好常见的学历、学校关键词、专业、技能,用于快速匹配,适配IT行业招聘场景(可根据需求添加,如“大数据”“云计算”)。
self.email_pattern:正则匹配标准邮箱格式(含@、域名,兼容各种邮箱后缀);
电话抽取逻辑:
•self.phone_pattern.findall(text):从文本中匹配所有符合手机号格式的内容,返回列表;
•text.split(“\n”):将纯文本按换行分割成列表,便于按行识别信息(如学校、姓名);
•res[“phone”] = phones[0] if phones else “”:取第一个匹配到的手机号(简历通常只有一个手机号),无匹配则返回空。
邮箱抽取逻辑: 与手机号抽取逻辑一致,匹配所有符合邮箱格式的内容,取第一个,无匹配则返回空。
学历抽取逻辑: 遍历提前定义的学历列表,匹配到第一个学历(如“本科”)就停止,避免多学历冲突(简历通常只写最高学历)。
学校抽取逻辑: 按行遍历文本,只要某一行包含“大学”“学院”等关键词,就判定为学校信息,取第一行(简历学校通常在教育经历开头)。
专业抽取逻辑: 遍历提前定义的专业列表,匹配到第一个专业关键词(如“软件”“计算机”)就停止,适配IT招聘场景。
技能抽取逻辑: 遍历技能库,将简历中出现的所有技能都提取出来,存入列表(支持多技能提取,符合IT岗位多技能需求)。
(4)技能标准化模块(SKILL_MAP字典+normalize_skills函数)
# ====================== 3. 技能标准化 ======================
SKILL_MAP = {
"python": ["Python", "python", "py"],
"java": ["Java", "java"],
"mindspore": ["MindSpore", "昇思"],
"mysql": ["MySQL", "mysql"],
"大模型": ["大模型", "深度学习", "机器学习"]
}
核心解决“简历技能写法不统一”的痛点——不同候选人可能写“MindSpore”或“昇思”,写“Python”或“py”,标准化后便于企业人才库统一管理、精准筛选。
def normalize_skills(skill_list):
std = []
for s in skill_list:
for standard, alias in SKILL_MAP.items():
if s in alias:
std.append(standard)
break
else:
std.append(s)
return list(set(std))
SKILL_MAP字典:定义“标准技能标签”与“别名”的映射关系,key是标准标签,value是所有可能的别名,可根据招聘需求添加(如添加“docker”的别名)。
遍历抽取的原始技能列表(skill_list);
标准化逻辑:
else:若技能不在预设别名中,直接保留原始技能(避免遗漏特殊技能);
若技能在某一标准标签的别名列表中,就将标准标签加入新列表(如“昇思”对应标准标签“mindspore”);
list(set(std)):去重(避免同一技能被多次提取),返回标准化后的技能列表。
(5)运行入口
•return output:返回解析结果,便于后续模块调用(如第三篇的人才评分、岗位匹配)。
功能解析:程序的启动开关,可直接修改注释,切换运行模式:
# ====================== 直接运行 ======================
if __name__ == "__main__":
# resume_parse_pipeline() # 无文件测试
resume_parse_pipeline("简历.docx") # 有文件用这个
•注释的resume_parse_pipeline():无文件测试模式,取消注释后,直接运行代码,使用内置模拟简历测试;
•if name == “main”:表示只有当直接运行该脚本时,才执行下面的代码(避免被其他脚本导入时误执行);
•可修改参数:若解析PDF简历,将参数改为"简历.pdf"即可(如resume_parse_pipeline(“简历.pdf”))。
•启用的resume_parse_pipeline(“简历.docx”):本地文件解析模式,需将“简历.docx”放在代码同一文件夹,运行后解析该简历,输出结构化结果;
2. 文档读取模块
支持Word、PDF自动识别读取,无需人工复制文本,是智能招聘系统的数据入口。
3. 简历实体抽取(NER)
通过规则+关键词匹配,自动提取:
•姓名、电话、邮箱
•学历、学校、专业
•技能列表
这是HR筛选人才最核心的关键字段。
4. 技能标准化
解决简历写法不统一问题:
•MindSpore、昇思 → 统一为 mindspore
•大模型、深度学习 → 统一为 大模型
便于企业人才库统一管理、精准筛选。
5. 结构化输出
输出标准JSON格式,可直接对接:
•招聘后台管理系统
•人才数据库
•AI岗位匹配引擎
四、运行结果与说明
运行代码后,控制台输出结构化简历数据,如下图所示:


