昇思 MindSpore 实战:智能简历解析系统搭建,Word/PDF信息自动抽取与结构化

在人力资源招聘场景中,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岗位匹配引擎

四、运行结果与说明
运行代码后,控制台输出结构化简历数据,如下图所示: