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


    8月份已经正式离职,这两个月主要在做新书校对工作。9月份陆续投了几份简历,参加了两次半面试,第一次是家做办公自动化的公司,开的薪水和招聘信息严重不符,感觉实在是在浪费时间,你说你给不了那么多为什还往上发布?第二次是家做业务系统的中型公司,结果面试我的技术总监直接被我按在地上摩擦,估计没戏了。还有半次,是个研究所,电话和微信简单沟通了一下,结果感觉自己有点被摩擦的意思,不愧挂着研究俩字。后两家公司的薪水区间几乎相同,但人员的技术水平却相差很大,这让我有些好奇,忍不住想分析一下苏州类似岗位的薪资水平。

    我在51job上搜索了一下苏州,计算机软件,互联网/电子商务,计算机服务,近一月的java相关职位,一共33页。

    第1页的URL是:

    https://search.51job.com/list/070300,000000,0000,00,3,99,java,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&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布局大概是这样:

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

      1 from urllib.request import urlopen
      2 from urllib.error import HTTPError
      3 from bs4 import BeautifulSoup
      4 import csv
      5 from itertools import chain
      6 import threading
      7 
      8 def get_jobs(url):
      9     '''
     10     根据url爬取职位信息
     11     :param url:
     12     :return: 职位列表,每个元素是一个四元组(职位名, 薪资,发布时间,详情页面url)
     13     '''
     14     try:
     15         html = urlopen(url)
     16     except HTTPError as e:
     17         print('Page was not found')
     18         return []
     19 
     20     jobs = []
     21     try:
     22         bsObj = BeautifulSoup(html.read())
     23         jobs_div = bsObj.find('div', {'id': 'resultList'}).findAll('div', {'class':'el'})
     24         for div in jobs_div[1:]:
     25             span_list = div.findAll('span')
     26             job_name = span_list[0].a.get_text().strip() # 职位名称
     27             job_url = span_list[0].a.attrs['href'].strip() # 职位详情url
     28             job_comp = span_list[1].a.get_text().strip() #公司名称
     29             job_salary = span_list[3].get_text().strip() # 薪资
     30             job_date = span_list[4].get_text().strip() # 日期
     31             jobs.append((job_comp, job_name, job_salary, job_date, job_url))
     32     except AttributeError as e:
     33         print(e)
     34         return []
     35     return jobs
     36 
     37 def crawl():
     38     '''
     39     分页苏州市近一月内的java相关职位
     40     :return: 职位列表,每个元素是一个四元组(职位名, 薪资,发布时间,详情页面url)
     41     '''
     42     # 查询条件:java;苏州;计算机软件、计算机服务(系统、数据服务、维修)、互联网/电子商务;近一月
     43     url = 'https://search.51job.com/list/070300,000000,0000,01%252C38%252C32,9,99,java,2,{0}.html?' 
     44           'lang=c&stype=&postchannel=0000&workyear=99&cotype=99&degreefrom=99' 
     45           '&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1' 
     46           '&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare='
     47     all_jobs = []
     48 
     49     def _crawl(page_start, page_end):
     50         '''
     51         分页爬取数据
     52         :param page_start: 起始页
     53         :param page_end: 终止页
     54         :return:
     55         '''
     56         print('crawl {0}~{1} start...'.format(page_start, page_end))
     57         for i in range(page_start, page_end):
     58             # 翻页url
     59             page_url = url.format(str(i))
     60             jobs = get_jobs(page_url)
     61             if len(jobs) == 0:
     62                 break
     63             all_jobs.append(jobs)
     64         print('crawl {0}~{1} over'.format(page_start, page_end))
     65 
     66     # 线程列表
     67     thread_list = []
     68     start_nums = list(range(0, 45, 5))
     69     end_nums = list(range(5, 50, 5))
     70     # 每5页一个线程, 最多50页
     71     for i in range(len(start_nums)):
     72         t = threading.Thread(target=_crawl, args=(start_nums[i], end_nums[i]))
     73         thread_list.append(t)
     74 
     75     print('开始爬取数据...')
     76     for t in thread_list:
     77         t.start()
     78     for t in thread_list:
     79         t.join()
     80     print('爬取结束')
     81 
     82     return all_jobs
     83 
     84 def save_data(all_jobs):
     85     '''
     86     将职位信息保存到joblist.csv
     87     :param all_jobs: 二维列表,每个元素是一页的职位信息
     88     '''
     89     print('正在保存数据...')
     90     with open('joblist.csv', 'w', encoding='utf-8', newline='') as fp:
     91         w = csv.writer(fp)
     92         # 将二维列表转换成一维
     93         t = list(chain(*all_jobs))
     94         w.writerows(t)
     95         print('保存结束,共{}条数据'.format(len(t)))
     96 
     97 if __name__ == '__main__':
     98     # 爬取数据
     99     all_jobs = crawl()
    100     # 保存数据
    101     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', '运维', '嵌入式','前端',这些职位都不要,’总监', '主管', '技术', '研发', '开发', '经理', 'java', 'JAVA', 'Java', '工程师’ ,这些需要保留。

    薪资的单位也不统一,有万/年,万/月,千/月, 统一转换成万/年,没写薪资的也不要。

     1 import csv
     2 from decimal import Decimal
     3 import pandas as pd
     4 import numpy as np
     5 
     6 def load_datas():
     7     '''
     8     从joblist.csv中装载数据
     9     :return: 数据集 datas
    10     '''
    11     datas = []
    12     with open('joblist.csv', encoding='utf-8') as fp:
    13         r = csv.reader(fp)
    14         for row in r:
    15             datas.append(row)
    16     return datas
    17 
    18 def clear(datas):
    19     '''
    20     数据清洗
    21     规则:
    22         1.没有标明薪资的,直接去掉;
    23         2.万/月和千/月转换成万/年
    24     :param datas: 原始数据
    25     :return: 清洗后的数据
    26     '''
    27     result = []
    28     for d in datas:
    29         # 清洗后的数据
    30         new_d = []
    31         new_d.append(d[0]) # 公司
    32         job = filter_job(d[1]) # 公司
    33         # 去掉公司不符合的数据
    34         if job == '':
    35             continue
    36         new_d.append(d[1]) # 职位
    37         salary_start, salary_end = salary_trans(d[2])
    38         # 去掉没写薪资的数据
    39         if salary_start == '':
    40             continue
    41         else:
    42             new_d.append(salary_start)
    43             new_d.append(salary_end)
    44         new_d.append(d[3]) # 发布日期
    45         new_d.append(d[4]) # 详细页面URL
    46         result.append(new_d)
    47     return result
    48 
    49 def filter_job(job):
    50     '''
    51     过滤职位名称
    52     :param job: 职位
    53     :return: 如果被过滤掉,返回''
    54     '''
    55     # 黑名单
    56     black = ['测试', '.Net', '运维', '嵌入式', '前端']
    57     # job在黑名单中
    58     if [job.find(x, 0, len(job)) for x in black].count(-1) < len(black):
    59         return ''
    60     # job在白名单
    61     white = ['总监', '主管', '技术', '研发', '开发', '经理', 'java', 'JAVA', 'Java', '工程师']
    62     if [job.find(x, 0, len(job)) for x in white].count(-1) > 0:
    63         return job
    64     return ''
    65 
    66 def salary_trans(salary):
    67     '''
    68     对薪资进行转换
    69     :param salary: 薪资
    70     :return: 二元组(起始年薪(万/年), 终止年薪(万/年))
    71     '''
    72     start, end = '', '' # 起始年薪, 终止年薪
    73     # 将所有薪资单位转换成 万/年
    74     if salary.endswith('万/年'):
    75         s = salary.replace('万/年', '').split('-')
    76         start, end = s[0], s[1]
    77     elif salary.endswith('万/月'):
    78         s = salary.replace('万/月', '').split('-')
    79         start = (Decimal(s[0]) * 12).normalize()
    80         end = (Decimal(s[1]) * 12).normalize()
    81     elif salary.endswith('千/月'):
    82         s = salary.replace('千/月', '').split('-')
    83         start = (Decimal(s[0]) * 12 / 10).normalize()
    84         end = (Decimal(s[1]) * 12 / 10).normalize()
    85     return str(start), str(end)
    86 
    87 if __name__ == '__main__':
    88     # 读取并清洗数据
    89     datas = np.array(clear(load_datas()))
    90     print(len(datas)) 

     还剩789条。

    分析开始。招聘信息上绝大多数都是以起薪资为准, 最高薪资就是做个样子,因此只分析起薪。先快速统计一下:

     1 def analysis(datas):
     2     ''' 数据分析 '''
     3     df = pd.DataFrame({'comp_name': datas[:, 0],
     4                        'job_name': datas[:, 1],
     5                        'salary_start': datas[:, 2],
     6                        'salary_end': datas[:, 3],
     7                        'publish_date': datas[:, 4],
     8                        'url': datas[:, 5]})
     9     # 全部起始薪资
    10     salary_col = df['salary_start']
    11     print('按起始薪资快速统计'.center(60, '-'))

     一共有789条记录,其中最多的是年薪12W,共出现了162次,大多数职位也就1W的月薪。

    再看起薪出现次数最多的top10。

     1 def analysis(datas):
     2     ''' 数据分析 '''
     3     df = pd.DataFrame({'comp_name': datas[:, 0],
     4                        'job_name': datas[:, 1],
     5                        'salary_start': datas[:, 2],
     6                        'salary_end': datas[:, 3],
     7                        'publish_date': datas[:, 4],
     8                        'url': datas[:, 5]})
     9     # 全部起始薪资
    10     salary_col = df['salary_start']
    11     print('按起始薪资快速统计'.center(60, '-'))
    12     # 按起始薪资快速统计
    13     print(salary_col.describe())
    14     # 起薪出现次数最多的top10
    15     salary_count_top_n(salary_col, 10)
    16 
    17 def salary_count_top_n(salary_col, n):
    18     ''' 起薪出现次数最多的top n '''
    19     print(('起薪出现次数最多的top' + str(n)).center(60, '-'))
    20     print('起薪	数量')
    21     # 起薪出现次数最多的top n
    22     count_top_n = salary_col.value_counts(sort=True, ascending=False).head(n)
    23     print(count_top_n)
    24     count_top_n.plot(kind='bar')
    25     plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    26     plt.xlabel('薪资(万/年)')
    27     plt.ylabel('职位数量')
    28     plt.show()

     真是一看吓一跳,月薪1W以下大概有500条,约占总职位数量的65%。

    再看起薪最高的top 10:

     2     ''' 数据分析 '''
     3     df = pd.DataFrame({'comp_name': datas[:, 0],
     4                        'job_name': datas[:, 1],
     5                        'salary_start': datas[:, 2],
     6                        'salary_end': datas[:, 3],
     7                        'publish_date': datas[:, 4],
     8                        'url': datas[:, 5]})
     9     ……
    10     # 起薪最高的top10
    11     urls = salary_high_top_n(df, 10)
    12 
    13 
    14 def salary_high_top_n(df, n):
    15     ''' 起薪最高的top n '''
    16     print(('起薪最高的top' + str(n)).center(60, '-'))
    17     salary_grp = df.groupby('salary_start')
    18     # 按起薪分组
    19     salary_top_n = sorted(salary_grp, reverse=True, key=lambda x: float(x[0]))[0:n]
    20     print('%-16s%-20s' % ('起薪', '数量'))
    21     # 职位对应的url
    22     urls = []
    23     for salary, group in salary_top_n:
    24         print('%-20s%-20d' % (salary, len(group)))
    25         urls +=  group.url.values.tolist()
    26     return urls

    top 10中共64个职位,年薪24W的占了26个,约占top10的40%,24W以下的占了65%以上。月薪2W居然都是高薪了,还有前途吗?


      作者:我是8位的

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

      本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途! 

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

  • 相关阅读:
    一些博弈
    中国剩余定理分析及扩展
    2018年全国多校算法寒假训练营练习比赛(第三场)
    数论——逆元
    扩展欧几里得
    算法思维题
    匈牙利算法
    Codeforces #449 div2 C题
    16级C程序设计竞赛C题
    动态规划--模板--hdu 1059 Dividing
  • 原文地址:https://www.cnblogs.com/bigmonkey/p/11588413.html
Copyright © 2020-2023  润新知