欢迎光临
我们一直在努力

写代码自动分析简历关键词,匹配招聘要求,颠覆海投没回音。

简历智能匹配系统 – 颠覆海投没回音

 

一、实际应用场景描述

 

目标用户:数字文化艺术创新创业课程的毕业生、文化创意行业求职者、转型期职场人

 

典型场景:

 

– 小李学完《数字艺术IP孵化》课程,想投递"文化科技公司产品经理"岗位,但不知道自己的简历哪些关键词匹配

– 小王有3年传统广告经验,想转行做"数字文创运营",海投50份简历只有2个面试,不知道问题在哪

– 小张应届毕业,面对"元宇宙内容策划"、"NFT运营专员"等新岗位,不知道如何包装自己的课程项目经历

 

传统痛点:

 

– 关键词盲区:不知道HR用ATS系统筛选哪些关键词

– 匹配度模糊:无法量化简历与目标岗位的契合程度

– 针对性不足:一份简历海投所有岗位,缺乏定制化

– 反馈缺失:投递后石沉大海,不知道哪里出了问题

 

二、核心痛点分析

 

痛点 影响 解决思路

关键词识别不准 ATS系统直接淘汰 多维度关键词提取+行业词典

匹配度无法量化 不知道改进方向 余弦相似度算法+技能矩阵

简历内容不聚焦 核心竞争力被埋没 智能高亮+缺失提醒

缺乏行业洞察 错失新兴岗位机会 动态行业词库+趋势分析

 

三、核心逻辑讲解

 

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐

│ 简历解析 │ –> │ 技能提取 │ –> │ JD解析 │ –> │ 智能匹配 │ –> │ 报告生成 │

│ (PyPDF2/ │ │ (NER+分词) │ │ (关键词挖掘) │ │ (相似度计算) │ │ (可视化) │

│ python-docx)│ │ │ │ │ │ │ │ │

└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘

       │ │ │ │ │

       ▼ ▼ ▼ ▼ ▼

  支持PDF/DOCX 识别硬技能+软技能 提取岗位核心要求 计算匹配分数 生成优化建议

  提取文本+格式 构建个人技能画像 分析公司业务方向 生成技能矩阵 可视化展示

 

关键技术点:

 

1. 简历解析:多格式支持,提取文本同时保留基础结构

2. 技能提取:结合命名实体识别(NER)和领域词典,精准识别技能

3. JD解析:基于职位描述的语义分析,提取硬性要求和加分项

4. 智能匹配:TF-IDF向量化+余弦相似度,量化匹配程度

5. 报告生成:可视化匹配结果,提供具体优化建议

 

四、代码实现

 

项目结构

 

resume_matcher/

├── resume_matcher.py # 主程序入口

├── resume_parser.py # 简历解析模块

├── skill_extractor.py # 技能提取模块

├── jd_analyzer.py # 职位描述分析模块

├── matcher_engine.py # 智能匹配引擎

├── report_generator.py # 报告生成模块

├── industry_dicts.py # 行业词典模块

├── config.py # 配置文件

├── requirements.txt # 依赖清单

└── README.md # 使用说明

 

1. config.py – 配置文件

 

"""

配置参数模块

包含文件路径、匹配规则、行业词典配置等

"""

 

# 文件路径配置

RESUME_PATH = "input/resume.pdf" # 输入简历路径

JD_PATH = "input/job_description.txt" # 职位描述文件路径

OUTPUT_DIR = "output" # 输出目录

 

# 匹配算法配置

MATCH_THRESHOLD = 0.6 # 基础匹配阈值

HIGH_WEIGHT_SKILLS = 2.0 # 高权重技能倍数

MEDIUM_WEIGHT_SKILLS = 1.5 # 中权重技能倍数

LOW_WEIGHT_SKILLS = 1.0 # 低权重技能倍数

 

# 行业配置

CURRENT_INDUSTRY = "digital_culture" # 当前行业

SUPPORTED_INDUSTRIES = [

    "digital_culture", # 数字文化

    "tech_product", # 科技产品

    "creative_design", # 创意设计

    "marketing_ops" # 营销运营

]

 

# 输出配置

GENERATE_REPORT = True # 是否生成详细报告

GENERATE_VISUALIZATION = True # 是否生成可视化

REPORT_FORMATS = ["html", "json"] # 报告格式

 

# 行业词典配置

INDUSTRY_DICT_PATHS = {

    "digital_culture": "dicts/digital_culture_skills.txt",

    "tech_product": "dicts/tech_product_skills.txt",

    "creative_design": "dicts/creative_design_skills.txt",

    "marketing_ops": "dicts/marketing_ops_skills.txt"

}

 

2. industry_dicts.py – 行业词典模块

 

"""

行业词典模块

包含各行业的技能词库、职位词库、工具词库

"""

 

import os

from typing import Dict, List, Set

import logging

 

# 配置日志

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

 

 

class IndustryDictionaries:

    """

    行业词典管理类

    负责加载和管理各行业的专业词汇

    """

    

    def __init__(self):

        """初始化行业词典"""

        self.dicts = {}

        self._load_default_dicts()

    

    def _load_default_dicts(self):

        """加载默认行业词典"""

        

        # 数字文化行业词典

        self.dicts["digital_culture"] = {

            "hard_skills": [

                # 核心技术

                "数字艺术", "IP孵化", "虚拟现实", "增强现实", "混合现实",

                "NFT", "区块链", "元宇宙", "数字孪生", "3D建模",

                "Unity3D", "Unreal Engine", "Blender", "Maya", "Cinema 4D",

                "数字策展", "文化数据分析", "用户画像", "内容运营",

                "AIGC", "生成式AI", "Midjourney", "Stable Diffusion", "GPT",

                # 平台工具

                "Steam", "Roblox", "Decentraland", "Sandbox", "OpenSea",

                "幻核", "鲸探", "百度超级链", "蚂蚁链", "腾讯云区块链"

            ],

            "soft_skills": [

                "文化敏感度", "创意策划", "跨界思维", "用户洞察",

                "内容敏感度", "审美能力", "故事叙述", "IP运营思维",

                "团队协作", "项目管理", "市场嗅觉", "创新思维"

            ],

            "tools": [

                "Photoshop", "Illustrator", "After Effects", "Premiere",

                "Figma", "Sketch", "Principle", "ProtoPie",

                "Python", "JavaScript", "HTML5", "CSS3",

                "SQL", "Tableau", "Power BI", "Excel"

            ],

            "positions": [

                "数字艺术设计师", "IP运营专员", "元宇宙内容策划",

                "NFT项目经理", "数字策展人", "文创产品经理",

                "虚拟空间设计师", "AIGC应用专家", "文化数据分析师"

            ]

        }

        

        # 科技产品行业词典

        self.dicts["tech_product"] = {

            "hard_skills": [

                "产品规划", "需求分析", "原型设计", "PRD撰写",

                "敏捷开发", "Scrum", "Kanban", "用户研究",

                "数据分析", "AB测试", "漏斗分析", "留存分析",

                "API设计", "数据库设计", "微服务架构", "云计算",

                "机器学习", "深度学习", "推荐算法", "自然语言处理"

            ],

            "soft_skills": [

                "逻辑思维", "沟通协调", "决策能力", "抗压能力",

                "同理心", "商业敏感度", "学习能力", "执行力"

            ],

            "tools": [

                "Axure", "墨刀", "Figma", "Sketch", "Jira",

                "Confluence", "Slack", "Notion", "Postman",

                "MySQL", "MongoDB", "Redis", "Docker", "Kubernetes"

            ],

            "positions": [

                "产品经理", "产品助理", "高级产品经理", "产品总监",

                "技术产品经理", "数据产品经理", "B端产品经理", "C端产品经理"

            ]

        }

        

        # 创意设计行业词典

        self.dicts["creative_design"] = {

            "hard_skills": [

                "品牌设计", "UI设计", "UX设计", "视觉设计",

                "交互设计", "动效设计", "插画设计", "字体设计",

                "版式设计", "色彩理论", "构图技巧", "设计系统",

                "响应式设计", "移动端设计", "网页设计", "电商设计"

            ],

            "soft_skills": [

                "审美能力", "创意思维", "细节把控", "趋势敏感度",

                "客户沟通", "提案能力", "时间管理", "自我驱动"

            ],

            "tools": [

                "Adobe Creative Suite", "Figma", "Sketch", "InVision",

                "Zeplin", "Abstract", "Procreate", "C4D",

                "Blender", "Cinema 4D", "Octane", "Redshift"

            ],

            "positions": [

                "UI设计师", "UX设计师", "视觉设计师", "品牌设计师",

                "动效设计师", "插画师", "设计主管", "创意总监"

            ]

        }

        

        # 营销运营行业词典

        self.dicts["marketing_ops"] = {

            "hard_skills": [

                "内容营销", "社交媒体运营", "社群运营", "私域运营",

                "活动策划", "用户增长", "转化优化", "SEO/SEM",

                "数据分析", "ROI分析", "LTV计算", "CAC分析",

                "邮件营销", "短信营销", "短视频运营", "直播运营"

            ],

            "soft_skills": [

                "文案能力", "网感", "用户洞察", "数据分析思维",

                "执行力", "创意策划", "危机公关", "跨部门协作"

            ],

            "tools": [

                "微信公众号", "微博", "抖音", "小红书", "B站",

                "企业微信", "钉钉", "飞书", "神策数据",

                "GrowingIO", "Google Analytics", "百度统计", "友盟+"

            ],

            "positions": [

                "内容运营", "社群运营", "用户运营", "活动运营",

                "新媒体运营", "增长黑客", "营销经理", "运营总监"

            ]

        }

        

        logger.info(f"已加载{len(self.dicts)}个行业词典")

    

    def get_dict(self, industry: str) -> Dict[str, List[str]]:

        """

        获取指定行业的词典

        

        Args:

            industry: 行业名称

            

        Returns:

            dict: 行业词典

        """

        if industry in self.dicts:

            return self.dicts[industry]

        else:

            logger.warning(f"未找到行业词典: {industry},使用默认词典")

            return self.dicts["tech_product"]

    

    def merge_dicts(self, industries: List[str]) -> Dict[str, List[str]]:

        """

        合并多个行业词典

        

        Args:

            industries: 行业名称列表

            

        Returns:

            dict: 合并后的词典

        """

        merged = {

            "hard_skills": [],

            "soft_skills": [],

            "tools": [],

            "positions": []

        }

        

        seen = {

            "hard_skills": set(),

            "soft_skills": set(),

            "tools": set(),

            "positions": set()

        }

        

        for industry in industries:

            industry_dict = self.get_dict(industry)

            for category in merged.keys():

                for item in industry_dict.get(category, []):

                    if item not in seen[category]:

                        merged[category].append(item)

                        seen[category].add(item)

        

        logger.info(f"合并词典完成,共{sum(len(v) for v in merged.values())}个词条")

        return merged

    

    def add_custom_words(self, industry: str, category: str, words: List[str]):

        """

        添加自定义词汇

        

        Args:

            industry: 行业名称

            category: 类别(hard_skills/soft_skills/tools/positions)

            words: 词汇列表

        """

        if industry not in self.dicts:

            self.dicts[industry] = {

                "hard_skills": [], "soft_skills": [], "tools": [], "positions": []

            }

        

        seen = set(self.dicts[industry].get(category, []))

        for word in words:

            if word not in seen:

                self.dicts[industry][category].append(word)

                seen.add(word)

        

        logger.info(f"已为{industry}行业添加{len(words)}个{category}词汇")

 

 

def demo_industry_dicts():

    """演示函数"""

    dict_manager = IndustryDictionaries()

    

    print("=== 数字文化行业硬技能 ===")

    for skill in dict_manager.dicts["digital_culture"]["hard_skills"][:10]:

        print(f" • {skill}")

    

    print("\\n=== 科技产品行业工具 ===")

    for tool in dict_manager.dicts["tech_product"]["tools"][:8]:

        print(f" • {tool}")

    

    print("\\n=== 合并数字文化+科技产品词典 ===")

    merged = dict_manager.merge_dicts(["digital_culture", "tech_product"])

    print(f" 硬技能总数: {len(merged['hard_skills'])}")

    print(f" 工具总数: {len(merged['tools'])}")

 

 

if __name__ == "__main__":

    demo_industry_dicts()

 

3. resume_parser.py – 简历解析模块

 

"""

简历解析模块

功能:支持PDF和DOCX格式,提取文本内容并保留基础结构

依赖:PyPDF2, python-docx

"""

 

import os

import re

from abc import ABC, abstractmethod

from typing import Dict, List, Optional, Tuple

import PyPDF2

from docx import Document

import logging

 

# 配置日志

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

 

 

class ResumeParser(ABC):

    """

    简历解析器抽象基类

    定义解析器的标准接口

    """

    

    @abstractmethod

    def parse(self, file_path: str) -> Dict:

        """解析简历文件"""

        pass

    

    @abstractmethod

    def extract_text(self, file_path: str) -> str:

        """提取纯文本"""

        pass

 

 

class PDFResumeParser(ResumeParser):

    """

    PDF格式简历解析器

    """

    

    def __init__(self):

        """初始化PDF解析器"""

        self.supported_extensions = ['.pdf']

    

    def parse(self, file_path: str) -> Dict:

        """

        解析PDF简历

        

        Args:

            file_path: PDF文件路径

            

        Returns:

            dict: 解析结果,包含文本、结构等信息

        """

        if not self._validate_file(file_path):

            return {"success": False, "error": "不支持的文件格式"}

        

        try:

            text = self.extract_text(file_path)

            structured_data = self._extract_structured_data(text)

            

            return {

                "success": True,

                "text": text,

                "structured_data": structured_data,

                "word_count": len(text),

                "format": "PDF"

            }

        except Exception as e:

            logger.error(f"PDF解析失败: {str(e)}")

            return {"success": False, "error": str(e)}

    

    def extract_text(self, file_path: str) -> str:

        """

        从PDF提取文本

        

        Args:

            file_path: PDF文件路径

            

        Returns:

            str: 提取的文本内容

        """

        text = ""

        

        with open(file_path, 'rb') as file:

            reader = PyPDF2.PdfReader(file)

            

            for page_num, page in enumerate(reader.pages):

                try:

                    page_text = page.extract_text()

                    if page_text:

                        text += f"\\n— 第{page_num + 1}页 —\\n"

                        text += page_text

                except Exception as e:

                    logger.warning(f"第{page_num + 1}页提取失败: {str(e)}")

        

        return text.strip()

    

    def _validate_file(self, file_path: str) -> bool:

        """验证文件格式"""

        _, ext = os.path.splitext(file_path)

        return ext.lower() in self.supported_extensions

    

    def _extract_structured_data(self, text: str) -> Dict:

        """

        从文本中提取结构化数据

        

        Args:

            text: 纯文本

            

        Returns:

            dict: 结构化数据

        """

        structured = {

            "contact_info": self._extract_contact_info(text),

            "sections": self._extract_sections(text),

            "education": self._extract_education(text),

            "experience": self._extract_experience(text)

        }

        return structured

    

    def _extract_contact_info(self, text: str) -> Dict:

        """提取联系信息"""

        contact = {

            "name": "",

            "email": "",

            "phone": "",

            "location": "",

            "linkedin": ""

        }

        

        # 提取邮箱

        email_pattern = r'[\\w\\.-]+@[\\w\\.-]+\\.\\w+'

        emails = re.findall(email_pattern, text)

        if emails:

            contact["email"] = emails[0]

        

        # 提取手机号

        phone_pattern = r'1[3-9]\\d{9}|(\\+\\d{1,3}[- ]?)?\\(?\\d{1,4}\\)?[- ]?\\d{1,4}[- ]?\\d{1,9}'

        phones = re.findall(phone_pattern, text)

        if phones:

            contact["phone"] = phones[0] if isinstance(phones[0], str) else phones[0][0]

        

        # 提取姓名(假设在文档开头)

        lines = text.split('\\n')

        for line in lines[:5]:

            line = line.strip()

            if len(line) >= 2 and len(line) <= 20 and not re.search(r'[@\\d]', line):

                contact["name"] = line

                break

        

        return contact

    

    def _extract_sections(self, text: str) -> List[Dict]:

        """提取简历章节"""

        sections = []

        section_patterns = [

            r'^(个人简介|自我介绍|Summary|Profile)',

            r'^(工作经历|工作经验|Experience|Work History)',

            r'^(教育背景|教育经历|Education|Academic)',

            r'^(技能专长|专业技能|Skills|Competencies)',

            r'^(项目经历|项目经验|Projects|Portfolio)',

            r'^(获奖荣誉|Awards|Honors)',

            r'^(自我评价|个人评价)'

        ]

        

        compiled_patterns = [re.compile(p, re.I) for p in section_patterns]

        

        lines = text.split('\\n')

        current_section = None

        

        for line in lines:

            line = line.strip()

            if not line:

                continue

            

            # 检查是否是章节标题

            is_section = False

            for pattern in compiled_patterns:

                if pattern.match(line):

                    if current_section:

                        sections.append(current_section)

                    current_section = {

                        "title": line,

                        "content": []

                    }

                    is_section = True

                    break

            

            if not is_section and current_section:

                current_section["content"].append(line)

        

        if current_section:

            sections.append(current_section)

        

        return sections

    

    def _extract_education(self, text: str) -> List[Dict]:

        """提取教育背景"""

        education = []

        edu_pattern = r'([\\u4e00-\\u9fa5]+大学|[\\u4e00-\\u9fa5]+学院|University|College).*?(本科|硕士|博士|MBA|Bachelor|Master|PhD)'

        matches = re.finditer(edu_pattern, text, re.I)

        

        for match in matches:

            edu_text = match.group(0)

            education.append({

                "raw_text": edu_text,

                "institution": match.group(1),

                "degree": match.group(2)

            })

        

        return education

    

    def _extract_experience(self, text: str) -> List[Dict]:

        """提取工作经历"""

        experience = []

        exp_pattern = r'(\\d{4}[年/-]\\d{1,2})\\s*[至至-—]\\s*(\\d{4}[年/-]\\d{1,2}|至今|present)'

        matches = list(re.finditer(exp_pattern, text))

        

        for i, match in enumerate(matches):

            start_date = match.group(1)

            end_date = match.group(2)

            

            # 提取时间段内的文本作为工作内容

            start_pos = match.start()

            end_pos = matches[i + 1].start() if i + 1 < len(matches) else len(text)

            work_content = text[start_pos:end_pos]

            

            experience.append({

                "start_date": start_date,

                "end_date": end_date,

                "content": work_content.strip()

            })

        

        return experience

 

 

class DOCXResumeParser(ResumeParser):

    """

    DOCX格式简历解析器

    """

    

    def __init__(self):

        """初始化DOCX解析器"""

        self.supported_extensions = ['.docx', '.doc']

    

    def parse(self, file_path: str) -> Dict:

        """

        解析DOCX简历

        

        Args:

            file_path: DOCX文件路径

            

        Returns:

            dict: 解析结果

        """

        if not self._validate_file(file_path):

            return {"success": False, "error": "不支持的文件格式"}

        

        try:

            doc = Document(file_path)

            text = self.extract_text(file_path)

            structured_data = self._extract_structured_data(doc, text)

            

            return {

                "success": True,

                "text": text,

                "structured_data": structured_data,

                "word_count": len(text),

                "format": "DOCX"

            }

        except Exception as e:

            logger.error(f"DOCX解析失败: {str(e)}")

            return {"success": False, "error": str(e)}

    

    def extract_text(self, file_path: str) -> str:

        """

        从DOCX提取文本

        

        Args:

            file_path: DOCX文件路径

            

        Returns:

            str: 提取的文本内容

        """

        doc = Document(file_path)

        text = ""

        

        for para in doc.paragraphs:

            text += para.text + "\\n"

        

        # 提取表格中的文本

        for table in doc.tables:

            for row in table.rows:

                for cell in row.cells:

                    text += cell.text + " "

                text += "\\n"

        

        return text.strip()

    

    def _validate_file(self, file_path: str) -> bool:

        """验证文件格式"""

        _, ext = os.path.splitext(file_path)

        return ext.lower() in self.supported_extensions

    

    def _extract_structured_data(self, doc, text: str) -> Dict:

        """从DOCX提取结构化数据"""

        structured = {

            "contact_info": self._extract_contact_info(text),

            "sections": self._extract_sections(doc),

            "education": [],

            "experience": []

        }

        return structured

    

    def _extract_contact_info(self, text: str) -> Dict:

        """提取联系信息(复用PDF解析器的方法)"""

        contact = {

            "name": "",

            "email": "",

            "phone": "",

            "location": "",

            "linkedin": ""

        }

        

        email_pattern = r'[\\w\\.-]+@[\\w\\.-]+\\.\\w+'

        emails = re.findall(email_pattern, text)

        if emails:

            contact["email"] = emails[0]

        

        phone_pattern = r'1[3-9]\\d{9}'

        phones = re.findall(phone_pattern, text)

        if phones:

            contact["phone"] = phones[0]

        

        lines = text.split('\\n')

        for line in lines[:5]:

            line = line.strip()

            if len(line) >= 2 and len(line) <= 20 and not re.search(r'[@\\d]', line):

                contact["name"] = line

                break

        

        return contact

    

    def _extract_sections(self, doc) -> List[Dict]:

        """从DOCX提取章节"""

        sections = []

        current_section = None

        

        for para in doc.paragraphs:

            text = para.text.strip()

            if not text:

                continue

利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!

            

       

赞(0)
未经允许不得转载:171主机测评 » 写代码自动分析简历关键词,匹配招聘要求,颠覆海投没回音。
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址