基于Flask的岗位就业可视化系统(py版本介绍)

🌟欢迎来到 我的博客 —— 探索技术的无限可能!


🌟博客的简介(文章目录)

前言

  • 本项目综合了基本数据分析的流程,包括数据采集(爬虫)、数据清洗、数据存储、数据前后端可视化等

  • 推荐阅读顺序为:数据采集——>数据清洗——>数据库存储——>基于Flask的前后端交互,有问题的话可以留言,有时间我会解疑~

  • 感谢阅读、点赞和关注

开发环境

  • 系统:Window 10 家庭中文版。
  • 语言:Python(3.9)、MySQL。
  • Python所需的库:pymysql、pandas、numpy、time、datetime、requests、etree、jieba、re、json、decimal、flask(没有的话pip安装一下就好)。
  • 编辑器:jupyter notebook、Pycharm、SQLyog。
    (如果下面代码在jupyter中运行不完全,建议直接使用Pycharm中运行)

文件说明

本项目下面有五个.py的文件,下面分别阐述各个文件所对应的功能:

  • 1. data_collection:分别从前程无忧网站和猎聘网上以关键词job_name爬取相关数据。其中,前程无忧爬取的数据主要用来进行相关图表的绘制;而猎聘网上主要为岗位要求文本数据,这部分进行词云的可视化展示。
  • 2. data_clean:对爬取到的数据进行清洗,包括去重去缺失值、变量重编码、特征字段创造、文本分词等。
  • 3. data_store:将清洗后的数据全部储存到MySQL中,其中对文本数据使用jieba.analyse下的extract_tags来获取文本中的关键词和权重大小,方便绘制词云。
  • 4. utils:大多为app调用MySQL数据库中的工具类函数;同时,里面也有引用data_collection、data_clean、data_store等函数,我们也主要使用该工具类进行岗位数据的爬取、清洗和存储。
  • 5. app:使用Python一个小型轻量的Flask框架来进行Web可视化系统的搭建,在static中有css和js文件,js中大多为百度开源的ECharts,再通过自定义controller.js来使用ajax调用flask已设定好的路由,将数据异步刷新到templates下的main.html中。
  • 6. 如何运行:先运行utils,提前进行数据采集、数据清洗、数据存储操作,之后更改app修改好datatable和job_name,这部分信息务必与utils中输入的保持一致(因为发现app一运行的话就会直接给出网页,所以没法在控制台上同步将变量赋值过去*_*)。
  • 7. 温馨提示:由于我在数据采集部分使用了一个用redis搭建的代理IP池,所以一开始运行的话需要将里面的proxies删掉,使用time.sleep即可(使用代理池能防止被封IP,同时可以更快爬取数据,实现可视化操作)。

技术栈

  • Python爬虫:(requests和xpath
  • 数据清洗:详细了解项目中数据预处理的步骤,包括去重去缺失值、变量重编码、特征字段创造和文本数据预处理 (pandas、numpy
  • 数据库知识:select、insert等操作,(增删查改&pymysql) 。
  • 前后端知识:(HTML、JQuery、JavaScript、Ajax)。
  • Flask知识:一个轻量级的Web框架,利用Python实现前后端交互。(Flask

备注:运行flask得到web链接时,在修改代码后想同步到web前端时一般需要将flask的debug功能打开,而且刷新时建议直接使用ctrl+F5强制刷新,因为浏览器在接收到本地主机部署的数据包后会存放在缓存中,方便下次加载时能快速呈现出来。

系统改进:之后可以尝试部署到网络中,同时,可以对自定义词典进行改进,增加更多岗位的分词信息;另外,也可以设置定时定点爬取数据,增加登录注册页面,让多台主机同时进行工作,实现数据的快速传递和更新。

data_collection

import requests
import re
import json
import time
import pandas as pd
from lxml import etree

proxypool_url = 'http://127.0.0.1:5555/random'
# 定义获取ip_pool中IP的随机函数
def get_random_proxy():
    ...
    return proxies


# 获取每个岗位的字段信息
def job51(datatable, job_name, page):
    # 浏览器伪装
    ...
    
    # 爬取每个页面下所有数据
    ...
    # 保存数据到DataFrame
    ...
    # 保存数据到csv文件中
    df.to_csv('xxx.csv')


# 获取每个岗位对应的详细要求文本
def liepin(datatable, job_name, page):
    # 浏览器伪装和相关参数
    ...
    # 遍历每一页上的数据
    ...
    # 遍历每一个岗位的数据
    ...
    # 保存数据
    df.to_csv('xxx.csv')


data_clean

import jieba
import re
import numpy as np
import pandas as pd


def job51_clean(datatable):
    # 读取数据
    data = pd.read_csv(        )
    # 去除重复值
    data.drop_duplicates(inplace = True)
    # 索引重置
    data.index = range(data.shape[0])

    # 接下来,我们对薪水这一列的表示方法都统一规范为数字(千/月)
    ...

    # 对地域这一列进行字段重编码
    data['地区'] = data['地域'].apply(lambda x: x.split('-')[0])
    # 进行省份的划分
    ...
    # 对31个省市进行遍历
    ...

    # 生成人数这一列
    ...

    # 生成学历这一列
    ...

    # 生成经验这一列
    ...

    # 去除无意义的字段
    ...


# 自定义函数来实现分词和去除停用词操作
def m_cut(tmpstr, stoplist):
    return [w.strip() for w in jieba.lcut(tmpstr) if w not in stoplist and len(w) > 1]


def liepin_clean(datatable):
     #同def job51_clean(datatable)


data_store

import pandas as pd
import numpy as np
import jieba.analyse as aa
import pymysql


# 连接数据库,需要先在数据库中定义好一张表
def get_con():
    con= pymysql.connect(host='localhost', user='用户名', password='密码', database='数据库名', charset='utf8')
    cursor = con.cursor()
    return con, cursor


# 关闭数据库
def con_close(con, cursor):
    if cursor:
        cursor.close()
    if con:
        con.close()


def job51_store(datatable):
   # 将每行数据都转变为tuple数据类型,然后遍历把每条数据都添加到sql中,有多次存数因而不使用上方函数
   ...


def liepin_store(datatable):
    # 下面把词云部分数据也存放进数据库中
    # 对文本进行去重操作
    ...


utils

import numpy as np
import pymysql
import time
import datetime
import os
import data_collection
import data_clean
import data_store


# 连接数据库,需要先在数据库中定义好一张表
def get_con():
    connect = pymysql.connect(host='localhost', user='用户名', password='密码', database='数据库名', charset='utf8')
    cursor = connect.cursor()
    return connect, cursor


# 关闭数据库
def con_close(connect, cursor):
    if cursor:
        cursor.close()
    if connect:
        connect.close()


# 定义函数
def query(sql):
    ...


def get_c1_data(datatable):
    ...

def get_c2_data(datatable):
    ...


def get_l1_data(datatable):
    ...


def get_l2_data(datatable):
    ...


def get_r1_data(datatable):
    ...



def get_r2_data(datatable):
    ...


def create_datatable(datatable):
    ...
    return '数据表已创建完毕!'


def exist_table(datatable):
    sql = f"show tables"
    res = query(sql)
    for i in range(len(res)):
        if datatable in res[i][0]:
            return '数据库存在此表!'
    i = input('数据库中不存在此表,是否需要重新创建?是为1,否为0:')
    if i == '1':
        path = './data/'
        os.mkdir(path + datatable)
        return create_datatable(datatable)
    else:
        return '已安全退出!'


if __name__ == '__main__':
    datatable = input('请输入你想保存的数据表单名称(英文名称):')
    ss = exist_table(datatable)
    k = input('你是否想要对数据进行更新或插入数据?是为1,否为0:')
    if ss == '数据表已创建完毕' or k == '1':
        start_time = datetime.datetime.now()
        print(f'系统启动时间为:{start_time}')
        print('*' * 50)
        print('本系统将依次进行数据采集、数据清洗、数据存储操作,下面先进行数据的采集操作')
        # 通过输入岗位名称和页数来爬取对应的网页内容
        job_name = input('请输入你想要查询的岗位(中文名称):')
        page = input('请输入你想要下载的页数(建议10-100页之间):')
        data_collection.job51(datatable, job_name, page)
        # 默认在猎聘网站上只爬取8页,一页有40个招聘岗位,而且大多数为文本,这部分信息不要求太多
        data_collection.liepin(datatable, job_name, '8')
        print('*' * 50)
        print('数据采集完毕,下面进行数据的清洗操作')
        data_clean.job51_clean(datatable)
        data_clean.liepin_clean(datatable)
        print('*' * 50)
        print('数据清洗完毕,下面进行数据的存储操作~')
        data_store.job51_store(datatable)
        data_store.liepin_store(datatable)
        print('*' * 50)
        print('数据存储完毕,接下来需要您修改app.py中的datatable和job_name,之后运行该文件进行该招聘岗位的可视化展示~ლ(╹◡╹ლ)')
        end_time = datetime.datetime.now()
        print(f'进行数据采集、数据清洗和数据储存共耗费:{end_time - start_time}')

app

from flask import Flask as _Flask, jsonify, render_template
from flask.json import JSONEncoder as _JSONEncoder
import decimal
import utils


class JSONEncoder(_JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return float(o)
        super(_JSONEncoder, self).default(o)


class Flask(_Flask): 
    json_encoder = JSONEncoder


app = Flask(__name__)
datatable = 'data_mining'
job_name = '数据挖掘'


# 路由解析,每映射到一个路由就调用一个函数
@app.route('/')
def index():
    return render_template("main.html")


@app.route('/title')
def get_title1():
    return job_name


# 获取系统当前时间,每隔1s刷新一次
@app.route('/time')
def get_time1():
    return utils.get_time()


# 对数据库中的数据进行计数、薪资取平均值、省份和学历取众数
@app.route('/c1')
def get_c1_data1():
    data = utils.get_c1_data(datatable)
    return jsonify({"employ": data[0], "avg_salary": data[1], "province": data[2], "edu": data[3]})


# 对省份进行分组,之后统计其个数,使用jsonify来将数据传输给ajax(中国地图)
@app.route('/c2')
def get_c2_data1():
    res = []
    for tup in utils.get_c2_data(datatable):
        res.append({"name": tup[0], "value": int(tup[1])})
    return jsonify({"data": res})


# 统计每个学历下公司数量和平均薪资(上下坐标折线图)
@app.route('/l1')
def get_l1_data1():
    data = utils.get_l1_data(datatable)
    edu, sum_company, avg_salary = [], [], []
    for s in data:
        edu.append(s[0])
        sum_company.append(int(s[1]))
        avg_salary.append(float(s[2]))
    return jsonify({"edu": edu, "sum_company": sum_company, "avg_salary": avg_salary})


# 统计不同学历下公司所招人数和平均经验(折线混柱图)
@app.route('/l2')
def get_l2_data1():
    data = utils.get_l2_data(datatable)
    edu, num, exp = [], [], []
    for s in data:
        edu.append(s[0])
        num.append(float(s[1]))
        exp.append(float(s[2]))
    return jsonify({'edu': edu, 'num': num, 'exp': exp})


# 统计不同类型公司所占的数量(饼图)
@app.route('/r1')
def get_r1_data1():
    res = []
    for tup in utils.get_r1_data(datatable):
        res.append({"name": tup[0], "value": int(tup[1])})
    return jsonify({"data": res})


# 使用jieba.analyse下的extract_tags来获取全部文本的关键词和权重,再用echarts来可视化词云
@app.route('/r2')
def get_r2_data1():
    cloud = []
    text, weight = utils.get_r2_data(datatable)
    for i in range(len(text)):
        cloud.append({'name': text[i], 'value': weight[i]})
    return jsonify({"kws": cloud})


if __name__ == '__main__':
    app.run()

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZShiJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值