基于Django的智能职位推荐系统设计与实现:从数据爬取到协同过滤推荐

基于Django的智能职位推荐系统设计与实现:从数据爬取到协同过滤推荐

本文详细介绍了如何使用Django框架构建一个完整的智能职位推荐系统,涵盖数据爬取、数据处理、推荐算法实现和Web界面开发的全流程。

📚 文章目录

  • 一、项目背景与需求分析
  • 二、技术选型与架构设计
  • 三、数据库设计
  • 四、数据爬取模块实现
  • 五、推荐算法核心实现
  • 六、Web界面开发
  • 七、数据分析与可视化
  • 八、系统部署与优化
  • 九、项目总结与思考

一、项目背景与需求分析

1.1 项目背景

在互联网招聘快速发展的今天,求职者往往面临信息过载的问题。如何在海量职位信息中找到最适合自己的职位,成为求职者的一大痛点。本项目旨在构建一个智能职位推荐系统,通过分析用户的投递行为和求职意向,为用户提供个性化的职位推荐服务。

1.2 功能需求

  • 数据获取:从招聘网站爬取职位信息
  • 数据管理:职位信息的存储、查询和更新
  • 智能推荐:基于用户行为提供个性化推荐
  • 搜索筛选:多条件职位搜索和筛选
  • 数据分析:职位数据的多维度统计分析
  • 用户管理:用户注册、登录、投递记录管理

1.3 技术难点

  1. 反爬虫机制:招聘网站的反爬虫策略需要处理
  2. 推荐算法:协同过滤算法的实现和优化
  3. 大数据处理:海量职位数据的高效查询和存储
  4. 用户体验:快速响应的界面和流畅的交互体验

二、技术选型与架构设计

2.1 技术栈

后端技术
  • Django 3.2.23:成熟的Python Web框架,提供完整的MVC架构
  • MySQL 8.0:关系型数据库,存储结构化数据
  • Selenium 4.3.0:浏览器自动化工具,用于数据爬取
  • Pandas + NumPy:数据处理和分析
  • PyEcharts:数据可视化
前端技术
  • Layui Admin:轻量级前端UI框架
  • ECharts:图表可视化库

2.2 系统架构

复制代码
┌─────────────────┐
│   用户浏览器     │
└────────┬────────┘
         │ HTTP请求
┌────────▼────────┐
│   Django Web层  │
│  (Views/URLs)   │
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
┌───▼───┐ ┌──▼──────┐
│业务逻辑│ │推荐算法  │
└───┬───┘ └──┬──────┘
    │        │
┌───▼────────▼───┐
│  Django ORM   │
└───────┬───────┘
        │
┌───────▼───────┐
│  MySQL数据库  │
└───────────────┘

2.3 项目目录结构

复制代码
JobRecommend/
├── JobRecommend/          # 项目配置
│   ├── settings.py       # 项目设置
│   └── urls.py           # 路由配置
├── job/                  # 主应用
│   ├── models.py         # 数据模型
│   ├── views.py          # 视图函数
│   ├── job_recommend.py  # 推荐算法
│   └── tools.py          # 爬虫工具
├── templates/            # 模板文件
└── static/              # 静态文件

三、数据库设计

3.1 数据模型设计

职位信息表 (job_data)
python 复制代码
class JobData(models.Model):
    job_id = models.AutoField('职位ID', primary_key=True)
    name = models.CharField('职位名称', max_length=255)
    salary = models.CharField('薪资', max_length=255)
    place = models.CharField('工作地点', max_length=255)
    education = models.CharField('学历要求', max_length=255)
    experience = models.CharField('工作经验', max_length=255)
    company = models.CharField('公司名称', max_length=255)
    label = models.CharField('职位标签', max_length=255)
    scale = models.CharField('公司规模', max_length=255)
    href = models.CharField('职位链接', max_length=255)
    key_word = models.CharField('关键词', max_length=255)

设计要点

  • 使用 AutoField 作为主键,自动递增
  • 所有字段允许为空,提高数据容错性
  • key_word 字段用于推荐算法的关键词匹配
用户表 (user_list)
python 复制代码
class UserList(models.Model):
    user_id = models.CharField('用户ID', primary_key=True, max_length=11)
    user_name = models.CharField('用户名', max_length=255)
    pass_word = models.CharField('密码', max_length=255)
投递记录表 (send_list)
python 复制代码
class SendList(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserList', on_delete=models.CASCADE)
    job = models.ForeignKey('JobData', on_delete=models.CASCADE)
    
    class Meta:
        unique_together = (('user', 'job'),)  # 防止重复投递

设计要点

  • 使用 unique_together 约束防止用户重复投递同一职位
  • on_delete=models.CASCADE 确保数据一致性
用户意向表 (user_expect)
python 复制代码
class UserExpect(models.Model):
    expect_id = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserList', models.DO_NOTHING)
    key_word = models.CharField(max_length=255)  # 期望职位关键词
    place = models.CharField(max_length=255)     # 期望工作地点

3.2 数据库索引优化

为了提高查询性能,建议在以下字段上创建索引:

sql 复制代码
-- 职位表索引
CREATE INDEX idx_keyword ON job_data(key_word);
CREATE INDEX idx_place ON job_data(place);
CREATE INDEX idx_education ON job_data(education);

-- 投递记录表索引
CREATE INDEX idx_user_job ON send_list(user_id, job_id);

四、数据爬取模块实现

4.1 Selenium爬虫实现

核心爬虫函数
python 复制代码
def lieSpider(city="", all_page="", spider_code=0):
    """爬取猎聘网职位信息"""
    
    # 1. 配置Chrome浏览器
    chrome_options = Options()
    chrome_options.add_argument('--headless')  # 无头模式
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--no-sandbox')
    
    # 2. 初始化WebDriver
    driver = webdriver.Chrome(
        service=Service(ChromeDriverManager().install()),
        options=chrome_options
    )
    
    # 3. 关键词列表
    key_list = ['python', 'AI', '数据分析', '数据挖掘', 
                '大数据', '人工智能', '算法', 'java']
    
    # 4. 遍历关键词和页数
    for key in key_list:
        for page in range(all_page):
            # 构建URL
            url = f"https://www.liepin.com/zhaopin/?currentPage={page}&key={key}"
            
            # 爬取页面数据
            get_pages(url, driver)
            
            # 随机休眠,避免被封IP
            time.sleep(random.uniform(1, 3))
    
    driver.quit()
页面数据提取
python 复制代码
def get_pages(url, driver):
    """从页面提取职位信息"""
    driver.get(url)
    time.sleep(2)  # 等待页面加载
    
    # 使用lxml解析HTML
    html = driver.page_source
    tree = etree.HTML(html)
    
    # 提取职位列表
    job_items = tree.xpath('//div[@class="job-info"]')
    
    for item in job_items:
        job_data = {
            'name': extract_text(item, './/h3/a/text()'),
            'salary': extract_text(item, './/span[@class="salary"]/text()'),
            'place': extract_text(item, './/span[@class="place"]/text()'),
            'company': extract_text(item, './/div[@class="company"]/text()'),
            # ... 更多字段
        }
        
        # 保存到数据库
        save_job_to_db(job_data)

4.2 反爬虫应对策略

  1. 请求频率控制:在每次请求之间添加随机延迟
  2. User-Agent轮换:模拟不同浏览器的请求头
  3. Cookie管理:维护会话状态,避免频繁登录
  4. 代理IP池:大规模爬取时使用代理IP轮换

4.3 数据清洗与存储

python 复制代码
def save_job_to_db(job_data):
    """保存职位数据到数据库"""
    # 数据清洗
    job_data['salary'] = clean_salary(job_data['salary'])
    job_data['key_word'] = extract_keyword(job_data['name'])
    
    # 去重检查
    if not JobData.objects.filter(href=job_data['href']).exists():
        JobData.objects.create(**job_data)

五、推荐算法核心实现

5.1 协同过滤算法原理

协同过滤分为两类:

  1. 基于用户的协同过滤:找到相似用户,推荐他们喜欢的物品
  2. 基于物品的协同过滤:找到相似物品,推荐给用户

本项目采用基于物品的协同过滤,因为职位数量相对用户数量更稳定。

5.2 相似度计算

使用余弦相似度计算两个职位的相似度:

python 复制代码
def similarity(job1_id, job2_id):
    """计算两个职位的相似度(余弦相似度)"""
    # 获取投递job1的用户集合
    job1_users = set(SendList.objects.filter(job_id=job1_id)
                    .values_list('user_id', flat=True))
    
    # 获取投递job2的用户集合
    job2_users = set(SendList.objects.filter(job_id=job2_id)
                    .values_list('user_id', flat=True))
    
    # 计算交集和并集
    intersection = len(job1_users & job2_users)
    union_size = len(job1_users) * len(job2_users)
    
    # 余弦相似度公式
    if union_size == 0:
        return 0
    similar_value = intersection / sqrt(union_size)
    
    return similar_value

相似度公式说明

复制代码
similarity(A, B) = |A ∩ B| / √(|A| × |B|)
  • |A|:投递职位A的用户数
  • |B|:投递职位B的用户数
  • |A ∩ B|:同时投递职位A和B的用户数

5.3 推荐算法实现

完整推荐函数
python 复制代码
def recommend_by_item_id(user_id, k=9):
    """基于物品的协同过滤推荐算法"""
    
    # 1. 检查用户是否存在
    if not UserList.objects.filter(user_id=user_id).exists():
        return get_random_jobs(k)
    
    user = UserList.objects.get(user_id=user_id)
    
    # 2. 获取用户已投递的职位
    sent_job_ids = list(user.sendlist_set.values_list('job_id', flat=True))
    
    if not sent_job_ids:
        # 用户没有投递记录,使用关键词匹配
        return recommend_by_keyword(user_id, k)
    
    # 3. 提取用户偏好关键词
    keywords = JobData.objects.filter(job_id__in=sent_job_ids)
                .values_list('key_word', flat=True)
    
    # 统计关键词出现频率
    keyword_counts = Counter(keywords)
    top_keywords = [k for k, _ in keyword_counts.most_common(3)]
    
    # 4. 获取候选职位(未投递且包含热门关键词)
    candidate_jobs = JobData.objects.filter(
        ~Q(job_id__in=sent_job_ids),
        key_word__in=top_keywords
    )[:30]
    
    # 5. 计算相似度并排序
    similarity_scores = []
    for candidate_job in candidate_jobs:
        max_similarity = 0
        for sent_job_id in sent_job_ids:
            sim = similarity(candidate_job.job_id, sent_job_id)
            max_similarity = max(max_similarity, sim)
        
        similarity_scores.append((max_similarity, candidate_job))
    
    # 6. 按相似度降序排序,返回前k个
    similarity_scores.sort(key=lambda x: x[0], reverse=True)
    recommendations = [job for _, job in similarity_scores[:k]]
    
    return recommendations
关键词匹配推荐(冷启动问题)
python 复制代码
def recommend_by_keyword(user_id, k=9):
    """基于关键词的推荐(解决冷启动问题)"""
    try:
        # 获取用户意向
        user_expect = UserExpect.objects.filter(user_id=user_id).first()
        
        if user_expect:
            # 根据意向匹配职位
            jobs = JobData.objects.filter(
                name__icontains=user_expect.key_word,
                place__icontains=user_expect.place
            )[:k]
            
            if jobs:
                return list(jobs.values())
        
        # 无匹配则返回随机推荐
        return get_random_jobs(k)
    except:
        return get_random_jobs(k)

5.4 算法优化建议

  1. 时间衰减因子:考虑投递时间,最近的投递权重更高
  2. 隐式反馈:不仅考虑投递,还可以考虑浏览、收藏等行为
  3. 混合推荐:结合内容过滤和协同过滤
  4. 实时更新:用户新投递后实时更新推荐列表

六、Web界面开发

6.1 Django视图函数

职位列表视图
python 复制代码
def get_job_list(request):
    """获取职位列表(支持分页和多条件筛选)"""
    page = int(request.GET.get("page", 1))
    limit = int(request.GET.get("limit", 10))
    keyword = request.GET.get("keyword", "")
    price_min = request.GET.get("price_min", "")
    price_max = request.GET.get("price_max", "")
    
    # 构建查询
    query = JobData.objects.filter(name__icontains=keyword)
    
    # 薪资筛选
    if price_min or price_max:
        # 提取薪资格式并筛选
        # ...
    
    # 分页
    start = (page - 1) * limit
    jobs = query[start:start+limit]
    
    return JsonResponse({
        "code": 0,
        "count": query.count(),
        "data": list(jobs.values())
    })
推荐接口
python 复制代码
def get_recommend(request):
    """获取推荐职位"""
    user_id = request.session.get("user_id")
    recommend_list = job_recommend.recommend_by_item_id(user_id, 9)
    
    # 检查投递状态
    for job in recommend_list:
        is_sent = SendList.objects.filter(
            user_id=user_id,
            job_id=job['job_id']
        ).exists()
        job['send_key'] = 1 if is_sent else 0
    
    return render(request, "recommend.html", {'recommend_list': recommend_list})

6.2 前端页面实现

Layui表格展示
javascript 复制代码
// 职位列表表格初始化
table.render({
    elem: '#jobTable',
    url: '/get_job_list/',
    page: true,
    cols: [[
        {field: 'name', title: '职位名称', width: 200},
        {field: 'salary', title: '薪资', width: 120},
        {field: 'company', title: '公司', width: 200},
        {field: 'place', title: '地点', width: 120},
        {title: '操作', toolbar: '#actionBar', width: 150}
    ]],
    done: function(res, curr, count){
        // 渲染投递按钮状态
        renderSendButtons(res.data);
    }
});

// 投递职位
function sendJob(jobId) {
    $.ajax({
        url: '/send_job/',
        type: 'POST',
        data: {job_id: jobId, send_key: 0},
        success: function(res) {
            if(res.Code === 0) {
                layer.msg('投递成功');
                table.reload('jobTable');
            }
        }
    });
}

6.3 用户认证

python 复制代码
def login(request):
    """用户登录"""
    if request.method == "POST":
        user_id = request.POST.get('user')
        password = request.POST.get('password')
        
        user = UserList.objects.filter(
            user_id=user_id,
            pass_word=password
        ).first()
        
        if user:
            # 设置session
            request.session['user_id'] = user_id
            request.session['user_name'] = user.user_name
            return JsonResponse({'code': 0, 'msg': '登录成功'})
        else:
            return JsonResponse({'code': 1, 'msg': '账号或密码错误'})
    
    return render(request, "login.html")

七、数据分析与可视化

7.1 数据统计接口

python 复制代码
def get_pie(request):
    """获取统计数据(学历、薪资、经验、城市分布)"""
    
    # 学历分布
    edu_list = ['博士', '硕士', '本科', '大专', '不限']
    edu_data = []
    for edu in edu_list:
        count = JobData.objects.filter(education__icontains=edu).count()
        edu_data.append({'name': edu, 'value': count})
    
    # 薪资分布
    salary_ranges = [
        ('5K及以下', lambda x: x <= 5),
        ('5-10K', lambda x: 5 < x <= 10),
        ('10K-15K', lambda x: 10 < x <= 15),
        # ...
    ]
    
    salary_data = []
    for name, condition in salary_ranges:
        count = count_by_salary_condition(condition)
        salary_data.append({'name': name, 'value': count})
    
    return JsonResponse({
        'edu_data': edu_data,
        'salary_data': salary_data,
        # ...
    })

7.2 PyEcharts可视化

python 复制代码
from pyecharts.charts import Pie, Bar
from pyecharts import options as opts

def generate_edu_pie_chart(edu_data):
    """生成学历分布饼图"""
    pie = (
        Pie()
        .add(
            series_name="学历要求",
            data_pair=edu_data,
            radius=["40%", "70%"]
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="学历要求分布"),
            legend_opts=opts.LegendOpts(orient="vertical", pos_left="left")
        )
    )
    return pie.render_embed()
相关推荐
UR的出不克2 小时前
基于机器学习的足球比赛预测系统 - 完整开发教程
人工智能·爬虫·python·深度学习·机器学习
Remember_9932 小时前
Java 入门指南:从零开始掌握核心语法与编程思想
java·c语言·开发语言·ide·python·leetcode·eclipse
sheji34162 小时前
【开题答辩全过程】以 基于Python的街区医院管理系统的设计与实现为例,包含答辩的问题和答案
开发语言·python
我是一只小青蛙8882 小时前
快速找回AnacondaPrompt的3种方法
python
davawang2 小时前
字符串分割并展开成表格的SQL实现方法
sql·数据分析
小趴菜不能喝2 小时前
Linux 搭建SVN服务
linux·运维·svn
Blossom.1182 小时前
实时知识增强大模型:基于Flink的流式向量索引与动态RAG系统
大数据·运维·人工智能·python·flink·prompt·知识图谱
renhongxia12 小时前
数据可视化实战:用AI工具制作专业数据分析图表
人工智能·信息可视化·语言模型·自然语言处理·数据分析·制造
可爱又迷人的反派角色“yang”2 小时前
k8s(七)
java·linux·运维·docker·云原生·容器·kubernetes