writer.writerow(col)
for i1 in range(0, num): # 爬取前3页数据
page_num += 1
url = "https://search.51job.com/list/080000,000000,0000,00,9,99," + keyword + ",2," + str(
page_num) + ".html"
page_text = geturl(url)
all_list = get_data(page_text)
if len(all_list[0]) == 0:
print('没有搜索到职位信息')
break
else:
print('正在爬取第%d页' % page_num)
save_data(all_list, writer, search_num=len(all_list[0]))
finally:
csv_file.close()
##### 3.2 数据预处理
我们通过爬虫已经拿到了原始数据,接下来我们需要将这些数据清洗,去除异常值、缺失值,转换我们要的数据格式。
首先我们可以对工作地点进行整理,我们爬取的数据,默认都是浙江省,我们按照浙江省各个地级市进行工作地点转换,代码如下:
job = pd.read_csv(“51job.csv”, encoding=‘utf-8’)
df = pd.DataFrame(job)
dict_city = {‘杭州’: 0, ‘湖州’: 0, ‘绍兴’: 0, ‘宁波’: 0, ‘嘉兴’: 0, ‘丽水’: 0, ‘台州’: 0, ‘温州’: 0, ‘金华’: 0, ‘衢州’: 0, ‘舟山’: 0}
city = df.loc[:, “工作地点”]
print(city.shape[0])
for i in range(city.shape[0]):
# print(city[i])
for k, v in dict_city.items():
# print(k, v)
if k in city[i]:
dict_city[k] = dict_city[k] + 1
df.loc[i, “城市”] = k
break
print(list(dict_city.keys()))
print(list(dict_city.values()))
city_df = df[“城市”]
if np.any(pd.notnull(df[“城市”])):
df[“城市”].fillna(“其他”, inplace=True)
print(df[“城市”].value_counts())

接着我们需要将原始数据得到的工资进行格式转换,转换成 k/月固定格式,并将整理的薪资数据单独存储保存下来。主要代码如下:
def get_salary(salary):
if ‘-’ in salary: # 针对1-2万/月或者10-20万/年的情况,包含-
low_salary = re.findall(re.compile(‘(\d*.?\d+)’), salary)[0]
high_salary = re.findall(re.compile(‘(\d?.?\d+)’), salary)[1]
if u’万’ in salary and u’年’ in salary: # 单位统一成千/月的形式
low_salary = round(float(low_salary) / 12 * 10, 1)
high_salary = round(float(high_salary) / 12 * 10, 1)
elif u’万’ in salary and u’月’ in salary:
low_salary = float(low_salary) * 10
high_salary = float(high_salary) * 10
else: # 针对20万以上/年和100元/天这种情况,不包含-,取最低工资,没有最高工资
low_salary = re.findall(re.compile(‘(\d*.?\d+)’), salary)[0]
if u’万’ in salary and u’年’ in salary: # 单位统一成千/月的形式
low_salary = round(float(low_salary) / 12 * 10, 1)
elif u’万’ in salary and u’月’ in salary:
low_salary = float(low_salary) * 10
elif u’元’ in salary and u’天’ in salary:
low_salary = round(float(low_salary) / 1000 * 21, 1) # 每月工作日21天
elif u’元’ in salary and u’小时’ in salary:
low_salary = round(float(low_salary) / 1000 * 8 * 21, 1) # 每天8小时,每月工作日21天
high_salary = low_salary
return low_salary, high_salary
job = pd.read_csv(“51job_pre.csv”, encoding=‘utf-8’)
job_df = job.drop(“福利待遇”, axis=1)
job_df = job_df.dropna(axis=0, how=“any”)
for index, row in job_df.iterrows():
salary = row[“薪资水平”]
if salary: # 如果待遇这栏不为空,计算最低最高待遇
getsalary = get_salary(salary)
low_salary = getsalary[0]
high_salary = getsalary[1]
else:
low_salary = high_salary = “0”
job_df.loc[index, “最低工资(k)”] = low_salary
job_df.loc[index, “最高工资(k)”] = high_salary
job_df.loc[index, “平均工资(k)”] = round((float(low_salary) + float(high_salary)) / 2, 1)
job_df.to_csv(“./51job_pre2.csv”, index=False)

薪资整理结束之后,我们通过观察可以看到招聘条件列中有我们需要的学历和工作经验等数据,我们需要提取出单独的学历,工作经验等数据。代码如下:
job_df.to_csv(“./51job_pre2.csv”, index=False)
job_df = pd.read_csv(“51job_pre2.csv”, encoding=‘utf-8’)
job_df[“学历”] = job_df[“招聘条件”].apply(lambda x: re.findall(“本科|大专|高中|中专|硕士|博士|初中及以下”, x))
job_df[“工作经验”] = job_df[“招聘条件”].apply(lambda x: re.findall(r’,“.*经验”|,“在校生/应届生”', x))
job_df[“学历”] = job_df[“学历”].apply(func)
job_df[“工作经验”] = job_df[“工作经验”].apply(func2)
薪资水平,公司类型,招聘条件,工作地点,公司规模,主要业务,城市,最低工资(k),最高工资(k),平均工资(k)
job_df = job_df.drop([“薪资水平”, “招聘条件”, “工作地点”, “主要业务”, “城市”], axis=1)
job_df = job_df.dropna(axis=0, how=“any”)
job_df.to_csv(“./51job_analysis.csv”, index=False)
原始数据清洗整理完毕,我们就可以继续编写GUI的数据展示了。
##### 3.3 岗位数据展示
上面我们已经拿到了需要的数据,首先我们可以通过读取保存的数据表格,可视化展示岗位数据表格。代码主要如下:
def read_csv_define(csv_path):
x = tree.get_children()
for item in x:
tree.delete(item)
global job_df
job_df = pd.read_csv(csv_path, encoding=‘utf-8’)
# print(job_df.shape[0])
for i in range(job_df.shape[0]):
tree.insert(“”, “end”,
values=(i+1, job_df.loc[job_df.index[i], “岗位名称”], job_df.loc[job_df.index[i], “公司名称”],
job_df.loc[job_df.index[i], “公司类型”], job_df.loc[job_df.index[i], “公司规模”],
job_df.loc[job_df.index[i], “学历”],job_df.loc[job_df.index[i], “工作经验”],
job_df.loc[job_df.index[i], “最低工资(k)”], job_df.loc[job_df.index[i], “最高工资(k)”]))
def openFile(label, label_num):
sname = label.get()
num_str = label_num.get()
num = int(num_str)
rep.main(sname, int(num/50))
pre.main()
Filepath = “./51job_analysis.csv”
# 打开文件选择对话框
# Filepath = filedialog.askopenfilename(filetypes=[('表格', '\*.xls;\*.csv')]) #过滤文件后缀类型
# print(os.path.split(Filepath))
(filepath, tempfilename) = os.path.split(Filepath) # 右侧的值是元组类型
try:
# Filepath 当路径存在的时候继续
if Filepath:
# 传输excel表格的路径
# 调用读取数据的函数,趁着用户正在查看查询条件的时候 将数据注入到全局变量中 减少查询等待
# 由于两种表格文件的读取模块不同,需要做处理判断属于哪种文件类型,故采用下边的方式进行判断
# 从文件名中分离出后缀
(filename, extension) = os.path.splitext(tempfilename)
if extension == '.xls' or extension == '.XLS':
read_xls(Filepath)
elif extension == '.csv':
read_csv_define(Filepath)
else:
print('未选择任何文件!')
# exit\_program()
except Exception as e:
global job_df
job_df = pd.DataFrame()
tkinter.messagebox.showwarning('警告', '文件读取异常,请检查!')
print("ex:", e)
finally:
size_str.set("")
type_str.set("")
edu_str.set("")
canvas_spice.get_tk_widget().destroy()
canvas_spice_hist.get_tk_widget().destroy()
效果如下:

##### 3.4 薪资图表可视化
接下来我们可以对不同工作经验年限对应的薪资统计,绘制缩略折线图。代码如下:
def show_plot():
global canvas_spice
canvas_spice.get_tk_widget().destroy()
# 图像及画布
fig, ax = plt.subplots(figsize=(5, 3.2), dpi=100) # 图像比例
canvas_spice = FigureCanvasTkAgg(fig, root)
canvas_spice.get_tk_widget().place(x=5, y=400) # 放置位置
work_experience = round(job_df.groupby(by=‘工作经验’)[‘平均工资(k)’].mean(), 2)
experience_list = [“无需经验”, “1年经验”, “2年经验”, “3-4年经验”, “5-7年经验”]
experience_val = [work_experience[i] for i in experience_list]
x = range(len(experience_list))
y = experience_val
plt.plot(x, y)
plt.xticks(x, experience_list, fontsize=6)
plt.grid(True, linestyle=“–”, alpha=0.5)
plt.xlabel(“工作经验”, fontsize=8)
plt.ylabel(“平均工资(k)”, fontsize=8)
plt.title(“工作经验对应薪资折线图”, fontsize=8)
canvas_spice.draw()
canvas_spice.get_tk_widget().bind(“”, xFunc1)
通过统计岗位数据中平均薪资数据,绘制出薪资分布直方图,展示该职位的平均薪资分布情况。代码如下:
def show_hist():
global canvas_spice_hist
canvas_spice_hist.get_tk_widget().destroy()
# 图像及画布
fig_hist, ax_hist = plt.subplots(figsize=(5, 3.2), dpi=100) # 图像比例
canvas_spice_hist = FigureCanvasTkAgg(fig_hist, root)
canvas_spice_hist.get_tk_widget().place(x=520, y=400) # 放置位置
plt.hist(job_df[“平均工资(k)”].values, bins=10)
# 求出最小值
max_ = job_df[“平均工资(k)”].max()
min_ = job_df[“平均工资(k)”].min()
# 修改刻度
plt.xticks(np.linspace(min_, max_, num=11),fontsize=7)
# 添加网格
plt.grid()
plt.xlabel(“平均工资(k)”, fontsize=8)
plt.ylabel(“岗位数量”, fontsize=8)
plt.title(“工资分布直方图”, fontsize=8)
canvas_spice_hist.draw()
canvas_spice_hist.get_tk_widget().bind(“”, xFunc2)
效果如下:

除了主界面之外,我们在绘制完图表之后希望能直接弹窗预览展示,因此也需要一个用于浏览图片的界面与功能,这部分整体会放在后续预览保存模块讲解。
##### 3.5 岗位公司情况统计
我们还可以对不同的公司规模、类型、对岗位学历要求等进行数据分析展示:
def show_data():
company = job_df.loc[:, “公司类型”].value_counts()
type_name = list(company.index)
x = range(len(type_name))
content = “”
for item in x:
content += type_name[item] + ‘----’ + str(company[item]) + ‘\n’
type_str.set(content)
company = job_df.loc[:, "公司规模"].value_counts()
company_scale = company.index.to_list()
z = range(len(company_scale))
size_content = ""
for item in z:
size_content += company_scale[item] + '----' + str(company[item]) + '\n'
size_str.set(size_content)
education = job_df.loc[:, "学历"].value_counts()
education_scale = education.index.to_list()
y = range(len(education_scale))
edu_content = ""
for item in y:
edu_content += education_scale[item] + '----' + str(education[item]) + '\n'
edu_str.set(edu_content)
效果如下:

##### 3.6 预览保存
我们在前面有提到,对于绘制好的图表,希望可以弹出预览保存,这里实现这个功能,采用的是**画布绑定左键双击事件**,弹出的子窗体同样可以**绑定右键事件**, 通过**事件适配器**传递图片参数。
def handlerAdaptor(fun, **kwds):
return lambda event, fun= fun, kwds= kwds:fun(event, **kwds)
def xFunc2(event):
top = Toplevel()
top.title(‘图像导出’)
top.minsize(700, 450)
top.resizable(False, False)
# 得到屏幕宽度
sw = root.winfo_screenwidth()
# 得到屏幕高度
sh = root.winfo_screenheight()
ww = 700
wh = 450
x = (sw - ww) / 2
y = (sh - wh) / 2
top.geometry("%dx%d+%d+%d" % (ww, wh, x, y))
top.transient(root)
top.grab_set()
# 图像及画布
fig, ax = plt.subplots(figsize=(7, 4.5), dpi=100) # 图像比例
canvas_spice = FigureCanvasTkAgg(fig, top)
canvas_spice.get_tk_widget().place(x=1, y=1) # 放置位置
plt.hist(job_df["平均工资(k)"].values, bins=10)
# 求出最小值
max_ = job_df["平均工资(k)"].max()
min_ = job_df["平均工资(k)"].min()
# 修改刻度
plt.xticks(np.linspace(min_, max_, num=11), fontsize=10)
# 添加网格
plt.grid()
plt.xlabel("平均工资(k)", fontsize=12)
plt.ylabel("岗位数量", fontsize=12)
plt.title("工资分布直方图", fontsize=15)
plt.savefig("hist.png")
canvas_spice.draw()
canvas_spice.get_tk_widget().pack()
img_pl = Image.open("hist.png").copy()
os.remove("hist.png")
canvas_spice.get_tk_widget().bind("<Button-3>", handlerAdaptor(saveimg, img=img_pl))
效果如下:

然后我们可以对预览的图片进行保存:
def saveimg(event, img):
Filepath = filedialog.asksaveasfilename(filetypes=[(‘图像’, ‘*.png;’)])
if Filepath:
if not Filepath.endswith((‘.png’)):
Filepath += ‘.png’
# 保存获得的图像
img.save(Filepath, ‘png’)
tkinter.messagebox.showinfo(“提示”, “保存成功!”)
HWND = win32gui.GetFocus() # 获取当前窗口句柄
win32gui.PostMessage(HWND, win32con.WM_CLOSE, 0, 0)
至此,自制的职位分析器小工具就编码完成啦~
下面,我一键获取生成**设计师**职位的分析报告:

从上面的分析报告中,不难看出:
1. 设计师职业的薪资水平在工作经验5年以上,薪资会有较大的涨幅。
2. 行业的平均薪资分布主要在6k-14k左右。
3. 设计行业的公司大部分是民营,公司人数在500人以下。
4. 对于设计的学历要求大部分是大专及以上。


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
上,薪资会有较大的涨幅。
2. 行业的平均薪资分布主要在6k-14k左右。
3. 设计行业的公司大部分是民营,公司人数在500人以下。
4. 对于设计的学历要求大部分是大专及以上。
[外链图片转存中...(img-uxdD05PR-1714709061561)]
[外链图片转存中...(img-KPskrUk4-1714709061562)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

被折叠的 条评论
为什么被折叠?



