简介:本资源是一套面向计算机专业本科生课程设计与毕业设计的智能简历解析系统实现方案,聚焦招聘场景中简历结构化提取与人岗匹配两大核心需求。系统基于Python开发,集成Grok-beta大模型完成简历文本解析,输出标准化JSON数据;采用google-bert/bert-base-chinese模型融合语义相似度与结构化字段匹配策略,提升岗位-候选人匹配精度。压缩包共9个文件(6个Python主模块、1个YAML配置、1个MD说明文档、1个TXT停用词表),总大小仅19KB,轻量易部署,目录结构清晰,含主程序、轮询调度、匹配引擎、AI调用、提示词管理等完整功能模块。目前已有71人学习下载,适合需快速掌握大模型+传统NLP协同落地、理解端到端AI招聘工具链设计逻辑的初学者与课程实践者。 最近在整理以前课程设计的时候翻出来一个很有意思的项目——基于 Python 的智能简历解析系统,核心就两块:简历解析和人岗匹配。当时做这个项目就是为了解决一个特别真实的痛点:招聘方每天收到几百份 PDF、Word 简历,光是把姓名、电话、工作经历这些信息从不同格式的简历里扒出来,再判断候选人合不合适,就要耗费大量时间。
这个系统说白了就是给招聘场景做了一个"自动预处理"工具:把简历从非结构化的文本里抽取成结构化字段,再和岗位要求做匹配打分,最后输出一份候选人和岗位的匹配排序。适合正在做课程设计、毕业设计的学生,也适合想了解 NLP 工程落地、文本抽取和相似度匹配怎么结合到实际场景的开发者。这篇文章我会把整个项目的技术思路、核心实现、踩过的坑和调优经验完整写出来,照着这个思路,你完全能自己复现一套可用的简历解析系统。
1. 项目整体设计与技术选型
1.1 简历解析系统到底解决什么问题
先说清楚一个概念:简历解析不是"把 PDF 转成文字"这么简单。一份简历在招聘者眼里是有结构的——个人信息、教育经历、工作经历、项目经历、技能标签,每个模块都有明确的语义。但在程序眼里,简历只是一大段无规则的字符串,可能带换行、带表格、带特殊符号,甚至同一个字段在不同简历里有完全不同的表述方式。
比如"工作经验"这个信息,有的简历写"5年Java开发经验",有的写"五年后端研发",有的写"2018.06 - 至今 高级工程师"。要让机器理解这些表达,本质上是做信息抽取(Information Extraction),也就是从非结构化文本中识别出实体(姓名、公司名、学校名)和关系(工作年限、技能标签),再映射到固定的字段结构里。
而人岗匹配解决的是另一个问题:简历结构化了以后,怎么判断这个人和岗位合适不合适?这涉及技能关键词的匹配、工作年限的比对、学历门槛的筛选,以及一定程度的语义相似度计算。
这个项目的核心价值就是把这套流程自动化,替代人工初筛阶段 80% 以上的重复性阅读工作。我当时的目标很明确:做一个能跑通的完整闭环,既要有解析的准确率,又要有匹配的可解释性。
1.2 基于 Python 的技术栈选型
选 Python 做这个项目,不是因为它"简单",而是因为它在文本处理生态上确实无可替代。Python 在这类项目里有几大优势:
- 文本处理库丰富:pdfplumber、PyPDF2、python-docx、pandas、jieba,每个环节都有成熟方案。
- NLP 工具链完整:从正则到分词到向量化,sklearn 的 TF-IDF 和余弦相似度可以直接用,不用自己造轮子。
- 快速迭代:解析规则和匹配逻辑本身就需要大量试错,Python 的动态特性让调试成本低很多。
具体的技术方案我分成了三层:
- 文件解析层:用 pdfplumber 处理 PDF,用 python-docx 处理 .docx,用 re 处理 .txt。这份选型后来被证明是明智的,因为 pdfplumber 对文本型 PDF 的抽取效果比 PyPDF2 稳定很多,后面我会详细讲为什么。
- 信息抽取层:策略是"正则 + 规则引擎 + 关键词库",没有上 BERT 这类深度模型。原因很现实:课程设计场景下没有 GPU 资源,标注数据也很少,规则方法在简历这种半结构化文本上性价比非常高。
- 匹配层:组合了 TF-IDF 向量化、余弦相似度和权重评分卡。TF-IDF 负责技能描述的语义匹配,权重评分卡负责硬性条件的筛选和排序。
1.3 系统架构与模块划分
整个系统的处理流程我是按流水线设计的,一个模块的输出就是下一个模块的输入,方便单独调试也方便扩展:
第一层是文件读取模块,负责把不同格式的简历统一转成纯文本。第二层是文本预处理模块,负责去噪、编码转换、章节切分。第三层是信息抽取模块,从文本里提取结构化字段。第四层是岗位画像模块,把岗位描述转成标准的技能集合和硬性条件。第五层是匹配打分模块,计算简历和岗位的匹配度。最后是结果输出模块,把排序结果和命中明细展示出来。
模块之间通过 dict 传递数据,字段结构提前定义好,比如 candidate 对象里就包含 name、phone、email、education_list、work_list、skills 这些字段。这样设计的好处是,任何一个环节出了问题,都能快速定位到对应模块单独修,不会牵一发动全身。
2. 核心细节解析与实操要点
2.1 文件格式读取的坑
简历上传最常见的格式就是 PDF 和 Word,这两个格式埋的坑非常深,我逐个说。
PDF 分两种,一种叫"文本型 PDF",就是你用 Word 另存为 PDF 生成的,文字是真正的文本字符,这种用 pdfplumber 直接 extract_text() 就能拿到内容。另一种叫"扫描件 PDF"或者"图片型 PDF",本质是一张张图片,这种必须用 OCR 才能抽取,pdfplumber 抽出来是空的。课程设计阶段我直接选择了支持文本型 PDF,并在系统里做了检测逻辑:如果 extract_text() 返回的文本长度小于阈值,就提示"该简历为扫描件,暂不支持解析"。
Word 格式同样有版本问题,.doc 是旧版二进制格式,.docx 是新的 XML 格式。python-docx 只支持 .docx,遇到 .doc 需要先做格式转换。我当时的做法是在服务端装了一个转换组件,把 .doc 统一转成 .docx 再解析,避免用户上传老格式就报错。这个处理对提升系统的鲁棒性非常关键。
还有一个特别隐蔽的问题是 PDF 表格的读取。很多简历的工作经历是用表格排版的,pdfplumber 的 extract_text() 会把表格内容连在一起,导致"公司"和"职位"黏到一行里。后期我补充了 extract_tables() 做表格识别,按行列重组后再拼回文本,才解决了这个问题。
2.2 文本清洗与章节切分
拿到纯文本之后不能直接做抽取,因为里面的噪声太多了。常见的噪声包括各种分隔符(一长串横线、星号)、多余换行、乱码字符、中英文标点混用等。我的清洗流程是:先统一把全角字符转半角,这步很重要,因为中文简历里经常有全角逗号、冒号,不统一会对后面的正则匹配造成干扰。
然后做换行压缩。好多 PDF 抽取出来的文本一行只有一个词,或者一页之间断行很碎,我会用规则把"行尾没有句号、逗号、冒号且下一行首字符不是大写字母"的断行拼接起来。这一步一定要小心,拼接规则太激进会把两个不同的段落合并,太保守又起不到作用。我试过用"下一行是否以常见字段名开头"来辅助判断,效果会好些。
章节切分是信息抽取的前置步骤。简历通常有一些固定的章节标题,比如"教育背景"、"工作经历"、"技能特长"、"自我评价"。我维护了一个章节标题词表,用正则去匹配这些关键词加换行的位置,把整份简历切成带标签的段落块。有了段落块,后面抽取教育经历的时候就不用全篇扫描,直接在"教育背景"段落里操作,准确率会大幅提升。
2.3 个人信息抽取:正则与规则引擎
个人信息包括姓名、电话、邮箱、工作年限、所在地、求职意向这几个字段,抽取策略各不相同。
姓名是相对难抽的字段,因为没有一个统一的前置标志。我的处理策略是结合章节位置和条件判断:在简历开头部分找"姓名:xxx"这种显式写法,如果没有就找"姓名|名字"关键词后面跟着的中文词组。实在不行就用一个中文人名库做匹配,加上启发式判断(比如候选词长度在 2-4 个字、不是常见动词和名词)。这个方案做不到 100% 准确,但在课程设计场景里已经够用。
电话和邮箱就简单多了,是标准的正则匹配。电话支持手机号和座机号两种格式,手机号用1[3-9]\d{9}这个正则,注意要加边界判断,避免从一串数字里截取出错误号码。座机号匹配区号加号码,比如 010-12345678。邮箱的正则是[\w.\-]+@[\w\-]+(\.[\w\-]+)+,匹配完以后还要过滤掉图片链接里的假邮箱。
工作年限的抽取是一个难点,因为简历里不会直接写"我有 5 年经验",而是藏在工作经历的时间段里。我的做法是:先解析"xxxx.xx - xxxx.xx"格式的时间段,计算出每段工作经历的时长,把所有经历时长相加作为总工作年限。如果工作经历缺失,再退回去用正则匹配"X年工作经验"这种显式说法。
2.4 教育经历和工作经历的抽取
教育经历要抽的信息包括学校、专业、学历、时间。学校名和专业名都属于开放集合,正则很难覆盖全。我的做法是维护一个高校名录和一个专业关键词库,在"教育背景"段落里逐句扫描,先匹配时间区间,然后在这个区间附近查找高校关键词(比如"大学"、"学院"),再找专业关键词。
这里有个细节要注意:学历关键词(本科、硕士、博士)可能出现的位置不固定,可能在学校名前面也可能在后面,所以不能依赖固定顺序,要单独扫描整段文本,用优先级排序来确定最终学历字段。比如同时出现"硕士"和"本科"时,取最高学历。
工作经历的结构化稍微复杂一点,一个岗位上可能有公司名、职位名、时间段、工作内容四个字段。时间还是用正则找,公司名和职位名则通过职位关键词库("工程师"、"产品经理"、"设计师"等)和公司名后缀词库("有限公司"、"科技"、"集团"等)来匹配。工作内容是自由文本,不强行结构化,直接保留原始描述,供后续人岗匹配做语义分析。
到这里你会发现,整个信息抽取环节,真正消耗时间的是规则库的整理。高校名录、专业词库、技能关键词库、职位头衔词库,这些都是要靠积累的。我在项目里把这些词库都独立成了 JSON 文件,方便随时扩展,这也是一个很实用的工程习惯。
3. 实操过程与核心环节实现
3.1 预处理模块的完整代码逻辑
我先把预处理模块的核心逻辑展示一下,这个模块直接决定后续抽取效果的上限,所以每一步都有明确的目的性。下面是一段简化但完整的代码示例:
import re import unicodedata def clean_text(raw_text: str) -> str: # 全角转半角,统一标点 text = unicodedata.normalize('NFKC', raw_text) # 去掉多余空白字符 text = re.sub(r'[ \t]+', ' ', text) # 处理断行:行尾无标点且下一行非大写/章节词时拼接 lines = text.splitlines() merged_lines = [] for i, line in enumerate(lines): line = line.strip() if not line: continue if merged_lines and _should_merge(merged_lines[-1], line): merged_lines[-1] = merged_lines[-1] + line else: merged_lines.append(line) return '\n'.join(merged_lines) def _should_merge(prev_line: str, next_line: str) -> bool: # 如果前一行以句号/冒号/分号/问号结尾,则不拼接 if re.search(r'[。:;?!.]$', prev_line): return False # 如果下一行像章节标题,不拼接 if re.match(r'^(教育背景|工作经历|技能特长|自我评价|项目经历)', next_line): return False return True这段代码里unicodedata.normalize('NFKC')很关键,它会把全角字符统一转成半角,fullwidth这种全角英文字符和全角标点都会被转成标准半角,后面写正则的时候就不用同时考虑两种形态了。
_should_merge函数里我判断前一行结尾的标点集合,是有取舍的,因为中文文本里句号、问号、感叹号都表示一句话结束,逗号不一定,分号和冒号在简历里非常常见,比如"热爱技术:热衷于探索新框架"。这个规则在实际测试中能覆盖 80% 的断行问题,剩下的部分靠章节切分以后局部分析。
3.2 信息抽取的字段级实现
信息抽取我封装成一个个独立的函数,每个函数负责一个字段的提取,参数是文本,返回值是提取结果。以邮箱和电话为例,正则是这样写的:
import re EMAIL_PATTERN = re.compile(r'[\w.\-]+@[\w\-]+(\.[\w\-]+)+') PHONE_PATTERN = re.compile(r'(?<!\d)(1[3-9]\d{9})(?!\d)') LANDLINE_PATTERN = re.compile(r'(?<!\d)(0\d{2,3}-?\d{7,8})(?!\d)') def extract_email(text: str): match = EMAIL_PATTERN.search(text) if match: return match.group(0).strip() return None def extract_phone(text: str): match = PHONE_PATTERN.search(text) if match: return match.group(0) match = LANDLINE_PATTERN.search(text) if match: return match.group(0) return None电话的正则我特意加了(?<!\d)和(?!\d)这两个前后断言,避免从身份证号或 QQ 号这种长数字串里错误截取出手机号。这个细节在测试的时候救了我好几次,因为很多简历在页脚会有页码,页码有时会跟手机号连得很近。
教育经历抽取这块,我用了"时间区间定位 + 局部扫描"的思路。先从段落里拿所有月份区间,比如 "2016.09 - 2020.06",然后在每个区间后面取固定长度的窗口文本,在这个窗口里做高校名和专业名的匹配。这样做可以减少很多无意义的全局扫描。
def extract_education(edu_section: str): edu_list = [] time_pattern = re.compile(r'(\d{4}[./年]\d{0,2})?\s*[-—]\s*(\d{4}[./年]\d{0,2}|至今)') matches = list(time_pattern.finditer(edu_section)) for i, match in enumerate(matches): start = match.end() end = matches[i + 1].start() if i + 1 < len(matches) else len(edu_section) window = edu_section[start:end] school = find_school(window) major = find_major(window) degree = find_degree(window) edu_list.append({ 'time': match.group(0), 'school': school, 'major': major, 'degree': degree, }) return edu_list注意find_school和find_major是查词库函数,词库是从 JSON 文件加载到内存里的。窗口切分以后,匹配范围从整篇文本缩小到了两段经历之间的内容,这个方案在大学应届生简历(一般只有一段教育经历)和社招简历(可能有交换经历)上都验证过,效果还不错。
3.3 岗位画像构建与简历向量化
简历解析完成以后,人岗匹配就是重头戏了。岗位画像我用的是一个 JSON 结构来定义:
{ "job_title": "Python后端开发工程师", "hard_requirements": { "education": "本科", "experience_years": 3, "skills_required": ["python", "django", "flask", "mysql", "redis", "linux"], "skills_preferred": ["docker", "kubernetes", "celery", "elasticsearch"] } }硬性要求里分两块:必备技能和加分技能。必备技能不满足时直接淘汰或者大幅扣分,加分技能作为额外加分项。教育程度和年限作为门槛条件,不满足时直接标记为"不匹配"。
简历侧我用了一个类似的 Candidate 字典来存放解析结果,包含 skills 列表。这里的 skills 来源有两个:一是简历里"技能特长"部分的关键词,二是工作经历和项目描述里匹配到技能词库的词汇。
向量化这一步,我并没有只对 skills 做匹配,而是把"工作经历描述 + 项目经历描述 + 技能列表"拼成一个长文本,对岗位画像的"岗位职责 + 任职要求"也拼成一个长文本,然后分别做 TF-IDF 向量化。这样简历向量里才能保留更多语义信息,比如"负责设计高并发系统架构"这种描述虽然没有直接出现技术栈关键词,但和岗位要求里"系统架构设计"是有语义关联的。
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity def calc_resume_job_similarity(resume_text: str, job_text: str): vectorizer = TfidfVectorizer(token_pattern=r'\b\w+\b', min_df=1) tfidf_matrix = vectorizer.fit_transform([resume_text, job_text]) similarity = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2]) return similarity[0][0]3.4 综合评分与排序逻辑
相似度分数只能代表文本层面的语义距离,不能直接作为最终匹配度。因为简历和岗位在表达方式上差异太大了,岗位描述习惯写"负责",简历习惯写"参与"或"主导",这种表述差异会影响相似度计算。所以我设计了一套加权评分卡,最终分数由三部分构成:
第一是技能命中率,占比 50%。计算方式是在岗位必备技能里做命中统计,命中一个得基础分,再叠加上加分技能的权重。第二是硬性条件匹配度,占比 30%。学历满足得满分,超一级得满分,低一级扣 50% 分。工作年限大于等于要求得满分,低于要求按比例扣分。第三是文本相似度,占比 20%。把上面算出来的 TF-IDF 余弦相似度映射到 0-100 分。
最终分数公式为:final_score = 技能命中得分 * 0.5 + 硬性条件得分 * 0.3 + 文本相似度分 * 0.2。
要注意的是,技能命中得分不是简单地数有几个关键词,而是把每个技能关键词映射到岗位权重里再计算加权命中率。比如岗位要求 python 权重 15、django 权重 10、mysql 权重 10,候选人命中 python 和 mysql,那么技能命中得分就是 (15 + 10) / (15 + 10 + 10) = 71.4 分。
排序的时候,系统先筛掉硬性条件不达标的简历,然后对剩余简历按总分从高到低排序,输出前三名的匹配明细。输出格式我是直接用了表格展示,每一行包含候选人姓名、匹配分数、技能命中列表、缺失技能列表、相似度分。这样招聘者不仅能看到分数,还能快速知道"这个人差在哪里",这一块的可解释性做得比较好。
4. 常见问题与排查技巧实录
4.1 PDF 解析出来是空白或乱码
这应该是所有简历解析系统里最常踩的坑。空白的问题通常是遇到了扫描件 PDF,解决办法前面已经提到了,就是检测抽出来的文本长度,太短就提示不支持。乱码的问题基本是编码不统一,PDF 有内置的字体编码,pdfplumber 多数情况能处理好,但偶尔会遇到个别字显示成乱码方块。那个阶段我用的方案是加一个乱码比例检测,如果乱码字符占比超过阈值就转用 PyPDF2 重新抽取,两个库互补着来。
4.2 Word 的 .doc 文件读不进来
很多同学一开始只处理 .docx,测试时用的是老版本的简历,系统直接报错。这个问题标准解法是先将 .doc 转成 .docx,转换工具的可用性在不同系统上有差异,我在 Windows 上用的是 Word COM 组件,在 Linux 服务端用了反编译转换方案,这需要额外安装依赖。如果实在不行,也可以在文件上传阶段限制格式,提示用户另存为 .docx 后再上传。课程设计阶段可以这样限制,但在实际生产环境建议还是把转换做上。
4.3 中文分词的坑
技能关键词匹配如果直接用字符串 in 判断,会出现很多问题。比如简历里写了"C++"和"C#",如果技能关键词是"C",in 判断会全部命中,这就不对了。
我使用的方案是:先做统一标准化,把"C++"转成"cpp","C#"转成"csharp",然后设置一个技能关键词的最小长度阈值,长度小于等于 2 的关键词用前后边界判定。比如"c"这种单字符关键词,只有前后都不是字母或数字时才认为命中。
另外 jieba 分词在分词时经常把"机器学习"分成"机器"和"学习"两个词,导致技能库里的"机器学习"匹配不上。解决方案是加载自建的用户词典,把技能关键词都加进去,这样分词时就会保留完整词条。
import jieba skill_terms = ["机器学习", "深度学习", "自然语言处理", "推荐系统", "数据结构"] for term in skill_terms: jieba.add_word(term)4.4 同义词和缩写导致的漏匹配
几乎每一份简历里的技能名称都和岗位描述里的不完全一致。岗位写"numpy",简历写"NumPy",岗位写"软件工程",简历写"SE"。大小写问题通过统一转小写能解决一大半,但"numpy"和"NumPy"这种大小写差异需要标准化处理,"软件工程"和"SE"这种则要准备一张同义词映射表。我把常见的缩写和全称做成了一张同义词表,匹配前先做归一化,效果立竿见影。
4.5 匹配结果不符合预期的调优思路
如果匹配出来的结果和人工判断差距很大,第一个要排查的不是算法,而是数据。看岗位画像的 skill_required 是否覆盖了真实的岗位需求,再看简历解析的 skills 提取是否完整。很多时候问题出在简历解析阶段技能词库不全,漏了某个关键技术,导致匹配阶段直接缺项。
第二个要排查的是权重设置。如果学历权重占太高,应届生和有经验的人都排到后面去了,那就要适当调低硬性条件的占比。评分卡的好处就是参数可调,我在项目里把这些权重也做成了配置文件,方便测试不同组合的效果。
第三个要排查的是阈值。如果所有简历的得分都在 60-70 分区间,区分度不足,可能是因为技能权重分布太平均,让每个人都容易命中。这种时候要拉开必备技能和加分技能的权重差,或者把"文本相似度"的系数调低,让技能命中成为真正的决定因素。
5. 测试与效果评估
5.1 构建简历测试集
测试环节我用了两种数据集:一种是人工构造的"标准简历",覆盖教育背景、工作经历、技能特长、自我评价全模块;另一种是从公开渠道收集的真实脱敏简历,涵盖不同排版风格、不同职业技能方向。真实简历的好处是能检验系统的鲁棒性,坏处是标签需要人工标注,工作量不小。
我把评估分成三个维度:字段级抽取准确率、简历级解析成功率、匹配排序准确率。字段级准确率指的是每个字段抽取后的值和人工标注值是否一致,简历级成功率指的是所有必填字段是否都正确抽取,匹配排序准确率指的是系统输出的前三名里有多少是真正匹配这个岗位的候人。
5.2 测试结果分析
我在 40 份测试简历上的结果大致是:电话和邮箱的抽取准确率接近 100%,基本没出过差错。教育经历中专院校和时间的准确率在 85% 左右,最典型的问题是学校名带了"(985)"或者"211"这种标签,导致学校名字被错误包含。技能标签的提取准确率在 80%,主要问题就是同义词和缩写造成的漏匹配。匹配结果的 Top3 准确率在 75% 左右,这个数据还有提升空间,但已经能证明整个流程是通得过的。
5.3 提升准确率的几个方向
从测试结果反推,提升空间最大的是技能标签的提取和标准化。我后来加了两种策略:一是把技能词库分类,比如分成"语言类"、"框架类"、"数据库类"、"工具类",分类以后在抽取时可以做上下文辅助判断;二是对工作经历描述做"关键词 + 邻近名词"的二元匹配,比如在"负责开发"后面紧跟"数据仓库"时,即使"数据仓库"不在技能库里,也要尝试添加到技能列表里。
6. 课程设计之后的扩展方向
6.1 从规则引擎升级到模型抽取
现有的方案在结构化简历上效果不错,但遇到非主流排版的简历就会露怯。如果想往深做,可以引入 BERT 或 ERNIE 这类预训练语言模型做序列标注,把教育经历、工作经历、人名、地点这些实体用 BIO 标注出来。对课程设计来说这个跨度有点大,因为需要标注数据和显卡资源,但对想继续深入 NLP 的读者来说,这是一个很自然的技术路线延伸。
6.2 加上简历查重与造假检测
简历解析出来的结构化数据,天然可以用来做候选人的历史对比和查重。比如检测同一候选人是不是投了多个不同岗位但简历内容几乎一样,或者检测工作经历的时间段是否有重叠——重叠往往意味着造假或时间线不一致。这些在产线上都是很有实用价值的功能。
6.3 Web 服务化与 API 封装
课程设计通常做完即止,但如果有余力,建议把它封装成一个 Flask 或者 FastAPI 服务,对外提供 JSON 接口,配合一个简单的上传页面,就是一个完整的招聘辅助工具。这部分的工程经验对写简历和面试都很加分,毕竟能独立做完整闭环的人,在团队里的价值是不可替代的。
7. 我个人实操中的几点体会
写这个项目最大的体会是:信息抽取这类任务,算法不是瓶颈,工程化的细节才是。规则引擎的代码好写,难的是把各种边缘情况都考虑到——全角半角、PDF 乱码、Word 版本、同义词、缩写、排版差异,每一个细节都在消耗你真正的大部分写代码时间。但恰恰是这些细节,把一份"能跑"的代码变成一份"靠谱"的代码。
另外一个小建议是:一定要把词库和规则做成配置文件,不要硬编码在代码里。我在迭代过程中无数次改了技能词库和高校名录,如果没有配置文件管理,每改一次都要动主逻辑,非常容易出 bug。后期我把所有词库独立成 JSON,主代码几乎没动过,改词库即改行为,效率提升明显。
最后再分享一个调试技巧:处理完一份简历后,把中间结果逐步打印出来,比如清洗后的文本、切分后的段落、抽取后的字段。这样你一眼就能看出问题出在哪个环节,而不是等到匹配结果不对了再去倒查。我是踩过几次坑之后才养成了这个习惯,现在做任何文本处理项目都会先确保中间结果可见,再继续往下走。
本文还有配套的精品资源,点击获取