Advertisement

苏州市java岗位的薪资状况(1)

阅读量:

八月份已经正式离职,这两个月主要负责新书校对工作.九月份陆续投递了几份简历,参加了两次半面试.第一次是在一家专注于办公自动化公司的岗位,所开薪资与招聘信息存在明显不符之处,说实话真的浪费时间,你说你们为什么能发这么多招聘信息还往上要人?第二次是在另一家专注于业务系统的中型公司,结果我在面谈时发现技术总监被我当着面羞辱.剩下两家企业都是某研究机构 affiliated ones,通过电话或微信初步沟通了一下,感觉自己受到了不当对待也不足以说明问题.不过最后两家企业的薪资范围大致相当,但人员的技术水平却相差甚远,这让我不禁产生了疑惑

我在51job平台上进行了网络搜索,在苏州市区范围内寻找软件开发、电子商务领域以及IT服务相关的Java技术职位,并且总共提供了33个页面的招聘信息。

第1页的URL是:

https://search.51job.com/list/java=99&companysize=99&ord_field=...&dibiaoid=...&line=&welfare=

第2页是:

https://search.51job.com/list/070300,000000,0000,00,3,99,java,2,2.html?lang=c&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=

不同之处在 java2,1.html 和 java2,2.html 看来那个不同的数字就是翻页信息。

在浏览器F12一下,页面dom布局大概是这样:

于是爬取了一下全部数据:

复制代码
 from urllib.request import urlopen

    
 from urllib.error import HTTPError
    
 from bs4 import BeautifulSoup
    
 import csv
    
 from itertools import chain
    
 import threading
    
  
    
 def get_jobs(url):
    
     '''
    
     根据url爬取职位信息
    
     :param url:
    
     :return: 职位列表,每个元素是一个四元组(职位名, 薪资,发布时间,详情页面url)
    
     '''
    
     try:
    
     html = urlopen(url)
    
     except HTTPError as e:
    
     print('Page was not found')
    
     return []
    
  
    
     jobs = []
    
     try:
    
     bsObj = BeautifulSoup(html.read())
    
     jobs_div = bsObj.find('div', {'id': 'resultList'}).findAll('div', {'class':'el'})
    
     for div in jobs_div[1:]:
    
         span_list = div.findAll('span')
    
         job_name = span_list[0].a.get_text().strip() # 职位名称
    
         job_url = span_list[0].a.attrs['href'].strip() # 职位详情url
    
         job_comp = span_list[1].a.get_text().strip() #公司名称
    
         job_salary = span_list[3].get_text().strip() # 薪资
    
         job_date = span_list[4].get_text().strip() # 日期
    
         jobs.append((job_comp, job_name, job_salary, job_date, job_url))
    
     except AttributeError as e:
    
     print(e)
    
     return []
    
     return jobs
    
  
    
 def crawl():
    
     '''
    
     分页苏州市近一月内的java相关职位
    
     :return: 职位列表,每个元素是一个四元组(职位名, 薪资,发布时间,详情页面url)
    
     '''
    
     # 查询条件:java;苏州;计算机软件、计算机服务(系统、数据服务、维修)、互联网/电子商务;近一月
    
     url = 'https://search.51job.com/list/070300,000000,0000,01%252C38%252C32,9,99,java,2,{0}.html?' \
    
       'lang=c&stype=&postchannel=0000&workyear=99&cotype=99&degreefrom=99' \
    
       '&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1' \
    
       '&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare='
    
     all_jobs = []
    
  
    
     def _crawl(page_start, page_end):
    
     '''
    
     分页爬取数据
    
     :param page_start: 起始页
    
     :param page_end: 终止页
    
     :return:
    
     '''
    
     print('crawl {0}~{1} start...'.format(page_start, page_end))
    
     for i in range(page_start, page_end):
    
         # 翻页url
    
         page_url = url.format(str(i))
    
         jobs = get_jobs(page_url)
    
         if len(jobs) == 0:
    
             break
    
         all_jobs.append(jobs)
    
     print('crawl {0}~{1} over'.format(page_start, page_end))
    
  
    
     # 线程列表
    
     thread_list = []
    
     start_nums = list(range(0, 45, 5))
    
     end_nums = list(range(5, 50, 5))
    
     # 每5页一个线程, 最多50页
    
     for i in range(len(start_nums)):
    
     t = threading.Thread(target=_crawl, args=(start_nums[i], end_nums[i]))
    
     thread_list.append(t)
    
  
    
     print('开始爬取数据...')
    
     for t in thread_list:
    
     t.start()
    
     for t in thread_list:
    
     t.join()
    
     print('爬取结束')
    
  
    
     return all_jobs
    
  
    
 def save_data(all_jobs):
    
     '''
    
     将职位信息保存到joblist.csv
    
     :param all_jobs: 二维列表,每个元素是一页的职位信息
    
     '''
    
     print('正在保存数据...')
    
     with open('joblist.csv', 'w', encoding='utf-8', newline='') as fp:
    
     w = csv.writer(fp)
    
     # 将二维列表转换成一维
    
     t = list(chain(*all_jobs))
    
     w.writerows(t)
    
     print('保存结束,共{}条数据'.format(len(t)))
    
  
    
 if __name__ == '__main__':
    
     # 爬取数据
    
     all_jobs = crawl()
    
     # 保存数据
    
     save_data(all_jobs)

为了加快爬取速度,启动了多线程操作,并将获取的数据保存至joblist.csv文件中。统计数据显示,在过去的一个月内已收集了967份与Java相关的招聘信息!

打开csv,发现里面的数据并不太好:

……

复制代码
 江苏未至科技股份有限公司,实施工程师(苏州),4-8千/月,09-25,https://jobs.51job.com/suzhou-gxq/105762963.html?s=01&t=0

    
 江苏未至科技股份有限公司,交付工程师,6-7千/月,09-25,https://jobs.51job.com/suzhou-gxq/104253078.html?s=01&t=0
    
 易程创新科技有限公司苏州分公司,高级软件工程师,1-2万/月,09-25,https://jobs.51job.com/suzhou-gxq/100292396.html?s=01&t=0
    
 江苏未至科技股份有限公司,项目经理,0.8-1.6万/月,09-25,https://jobs.51job.com/suzhou-gxq/85646230.html?s=01&t=0
    
 达内时代教育集团,咨询顾问底薪4-7K+五险一金,1-1.5万/月,09-25,https://jobs.51job.com/suzhou-gsq/113505714.html?s=01&t=0
    
 达内时代教育集团,搜索顾问底薪4-7K-上市企业,1-1.5万/月,09-25,https://jobs.51job.com/suzhou-gsq/113505583.html?s=01&t=0
    
 苏州工业园区测绘地理信息有限公司...,Web前端开发工程师,6-15万/年,09-25,https://jobs.51job.com/suzhou-gyyq/86942466.html?s=01&t=0
    
 江苏云坤信息科技有限公司,项目经理,1-1.8万/月,09-25,https://jobs.51job.com/suzhou-gyyq/112994728.html?s=01&t=0
    
 江苏云坤信息科技有限公司,前端开发工程师,0.8-1.5万/月,09-25,https://jobs.51job.com/suzhou-gyyq/70761080.html?s=01&t=0
    
 苏州智享云信息科技有限公司,系统架构师,1.6-2.5万/月,09-25,https://jobs.51job.com/suzhou/108411172.html?s=01&t=0
    
 英诺赛科(苏州)半导体有限公司,MES 工程师,0.6-1万/月,09-25,https://jobs.51job.com/suzhou-wjq/115142646.html?s=01&t=0
    
 苏州麦芒软件科技有限公司,软件测试助理工程师,4-6千/月,09-25,https://jobs.51job.com/suzhou/115511688.html?s=01&t=0
    
 三门峡崤云信息服务股份有限公司,大数据挖掘工程师,0.8-2万/月,09-25,https://jobs.51job.com/sanmenxia/110394655.html?s=01&t=0
    
 苏州春慷咨询管理有限公司,软件实施工程师,0.3-1万/月,09-25,https://jobs.51job.com/suzhou-gsq/115617730.html?s=01&t=0
    
 苏州佑捷科技有限公司,高级开发工程师,1.5-2万/月,09-25,https://jobs.51job.com/suzhou-gxq/114727492.html?s=01&t=0
    
 苏州佑捷科技有限公司,Android开发工程师,1-2.5万/月,09-25,https://jobs.51job.com/suzhou-gxq/114726758.html?s=01&t=0
    
 瑞泰信息技术有限公司,.NET开发工程师(实习生),6.5-8.5千/月,09-25,https://jobs.51job.com/suzhou/108055469.html?s=01&t=0
    
 北京直真科技股份有限公司,前端开发工程师(苏州),1.1-1.7万/月,09-25,https://jobs.51job.com/suzhou/113018219.html?s=01&t=0

……

该职位涉及测试工作以及项目经理等管理角色,并且还包含Android开发人员之外的其他岗位如.NET开发人员。因此,在进行数据分析之前,请务必对不相关的岗位进行筛选排除。对于如‘测试’‘.Net’‘运维’‘嵌入式’‘前端’等职业类别,请予以排除。包括但不限于总监/主管/技术/研发/开发/经理等管理及技术岗位的所有成员均不在本次分析范围内

不同单位的薪资数据存在差异

复制代码
 import csv

    
 from decimal import Decimal
    
 import pandas as pd
    
 import numpy as np
    
  
    
 def load_datas():
    
     '''
    
     从joblist.csv中装载数据
    
     :return: 数据集 datas
    
     '''
    
     datas = []
    
     with open('joblist.csv', encoding='utf-8') as fp:
    
     r = csv.reader(fp)
    
     for row in r:
    
         datas.append(row)
    
     return datas
    
  
    
 def clear(datas):
    
     '''
    
     数据清洗
    
     规则:
    
     1.没有标明薪资的,直接去掉;
    
     2.万/月和千/月转换成万/年
    
     :param datas: 原始数据
    
     :return: 清洗后的数据
    
     '''
    
     result = []
    
     for d in datas:
    
     # 清洗后的数据
    
     new_d = []
    
     new_d.append(d[0]) # 公司
    
     job = filter_job(d[1]) # 公司
    
     # 去掉公司不符合的数据
    
     if job == '':
    
         continue
    
     new_d.append(d[1]) # 职位
    
     salary_start, salary_end = salary_trans(d[2])
    
     # 去掉没写薪资的数据
    
     if salary_start == '':
    
         continue
    
     else:
    
         new_d.append(salary_start)
    
         new_d.append(salary_end)
    
     new_d.append(d[3]) # 发布日期
    
     new_d.append(d[4]) # 详细页面URL
    
     result.append(new_d)
    
     return result
    
  
    
 def filter_job(job):
    
     '''
    
     过滤职位名称
    
     :param job: 职位
    
     :return: 如果被过滤掉,返回''
    
     '''
    
     # 黑名单
    
     black = ['测试', '.Net', '运维', '嵌入式', '前端']
    
     # job在黑名单中
    
     if [job.find(x, 0, len(job)) for x in black].count(-1) < len(black):
    
     return ''
    
     # job在白名单
    
     white = ['总监', '主管', '技术', '研发', '开发', '经理', 'java', 'JAVA', 'Java', '工程师']
    
     if [job.find(x, 0, len(job)) for x in white].count(-1) > 0:
    
     return job
    
     return ''
    
  
    
 def salary_trans(salary):
    
     '''
    
     对薪资进行数据中
    
     :param salary: 薪资
    
     :return: 二元组(起始年薪(万/年), 终止年薪(万/年))
    
     '''
    
     start, end = '', '' # 起始年薪, 终止年薪
    
     # 将所有薪资单位转换成 万/年
    
     if salary.endswith('万/年'):
    
     s = salary.replace('万/年', '').split('-')
    
     start, end = s[0], s[1]
    
     elif salary.endswith('万/月'):
    
     s = salary.replace('万/月', '').split('-')
    
     start = (Decimal(s[0]) * 12).normalize()
    
     end = (Decimal(s[1]) * 12).normalize()
    
     elif salary.endswith('千/月'):
    
     s = salary.replace('千/月', '').split('-')
    
     start = (Decimal(s[0]) * 12 / 10).normalize()
    
     end = (Decimal(s[1]) * 12 / 10).normalize()
    
     return start, end
    
  
    
 if __name__ == '__main__':
    
     # 读取并清洗数据
    
     datas = np.array(clear(load_datas()))
    
     print(len(datas))

还剩789条。

分析开始,分别看起始薪资和终止薪资的top5。

复制代码
 def analysis(datas):

    
     ''' 数据分析 '''
    
     df = pd.DataFrame({'comp_name': datas[:, 0],
    
                    'job_name': datas[:, 1],
    
                    'salary_start': datas[:, 2],
    
                    'salary_end': datas[:, 3],
    
                    'publish_date': datas[:, 4],
    
                    'url': datas[:, 5]})
    
  
    
     def _salary_analysis(row_idx, row_name):
    
     # 按薪资分组
    
     grp_salary = df.groupby(row_idx)
    
     # 按薪资排序
    
     grp_salary = sorted(grp_salary, key=lambda x: float(x[0]))
    
     print('按{}分组,共{}组不同的薪资'.format(row_name, len(grp_salary)).center(40, '-'))
    
     print('%-20s%-20s%-20s' % ('No.', row_idx, 'count'))
    
     for i, (salary, group) in enumerate(grp_salary):
    
         print('%-20d%-20s%-20d' % (i, salary, len(group)))
    
  
    
     # 薪资最高的 top5
    
     salary_top5 = sorted(grp_salary, reverse=True, key=lambda x: float(x[0]))
    
     print('\n', row_name, '最高的top5')
    
     print('%-20s%-20s%-20s' % ('No.', row_idx, 'count'))
    
     for i, (salary, group) in enumerate(salary_top5):
    
         if i == 5:
    
             break
    
         print('%-20d%-20s%-20d' % (i, salary, len(group)))
    
         # print(group)
    
  
    
     # 职位最多的 top5
    
     count_top5 = sorted(grp_salary, reverse=True, key=lambda x: len(x[1]))
    
     print('\n职位最多的top5')
    
     print('%-20s%-20s%-20s' % ('No.', row_idx, 'count'))
    
     for i, (salary, group) in enumerate(count_top5):
    
         if i == 5:
    
             break
    
         print('%-20d%-20s%-20d' % (i, salary, len(group)))
    
  
    
     _salary_analysis('salary_start', '起始薪资')
    
     print()
    
     _salary_analysis('salary_end', '终止薪资')

看来大多数职位的起始薪水是9.6~12万/年。

终止薪资的top5:

其中一个数据达到了每年360万元的水平确实让人难以置信,在 def analysis(datas) 的第28行取消注释设置后,从而能够轻松获取该数据。

中翔科技(杭州)有限公司,技术总监,20-30万/月,08-05,https://jobs.51job.com/suzhou/115839486.html?s=01&t=0

估计是把年薪写成月薪了。改正这条数据后再分析一次top5:

看来整体行业不是那么景气。

明天继续向下钻取信息,看看高薪岗位的共性。 睡觉。


作者:我是8位的

出处:http://www.cnblogs.com/bigmonkey

本文主要涉及学习、研究与分享的内容。如若需要转载,请联系作者并注明文章来源以及作者本人信息,并仅用于学术交流或教学辅助等非商业用途。

扫描二维码关注公作者众号“我是8位的”

全部评论 (0)

还没有任何评论哟~