Python数据分析--数据分析岗位最新招聘情况

本次主要围绕数据分析岗位的招聘情况, 进行一个简单的数据分析 环境 win8, python3.7, pycharm, jupyter notebook 正文 1. 明确分析目的 了解数据分析岗位的最新招聘情况, 包括地区分布, 学历要求, 经验要求, 薪资水平等. 2. 数据收集 这里借助爬虫, 爬取招聘网站的招聘信息, 进而分析出相关的薪资以及招聘要求. 2.1 目标站点分析 通过对目标站点的分析, 我们需要确定目标站点的请求方式, 以及网页结构. 2.2 新建scrapy项目 1. 在cmd命令行窗口中任意路径下执行以下代码, 比如在"D:\python\Tests"目录下新建zhaopin项目. d: cd D:\python\Tests scrapy startproject zhaopin 2. 在完成了zhaopin项目创建之后, 接下来就是在zhaopin项目文件夹中新建spider爬虫主程序 cd zhaopin scrapy genspider zhaopinSpider zhaopin.com 这样就完成项目zhaopin的创建, 开始编写我们的程序吧. 2.3 定义items 在items.py文件中定义需要爬取的招聘信息. 复制代码 import scrapy from scrapy.item import Item, Field class zhaopinItem(Item): # define the fields for your item here like: # name = scrapy.Field() JobTitle = Field() #职位名称 CompanyName = Field() #公司名称 CompanyNature = Field() #公司性质 CompanySize = Field() #公司规模 IndustryField = Field() #所属行业 Salary = Field() #薪水 Workplace = Field() #工作地点 Workyear = Field() #要求工作经验 Education = Field() #要求学历 RecruitNumbers = Field() #招聘人数 ReleaseTime = Field() #发布时间 Language = Field() #要求语言 Specialty = Field() #要求专业 PositionAdvantage = Field() #职位福利 复制代码 2.4 编写爬虫主程序 在zhaopinSpider.py文件中编写爬虫主程序 复制代码 import scrapy from scrapy.selector import Selector from scrapy.spiders import CrawlSpider from scrapy.http import Request from zhaopin.items import zhaopinItem class ZhaoPinSpider(scrapy.Spider): name = "ZhaoPinSpider" allowed_domains = ['zhaopin.com'] start_urls = ['https://xxxx.com/list/2,{0}.html?'.format(str(page)) for page in range(1, 217)] def parse(self, response): ''' 开始第一页 :param response: :return: ''' yield Request( url = response.url, callback = self.parse_job_url, meta={}, dont_filter= True ) def parse_job_url(self, response): ''' 获取每页的职位详情页url :param response: :return: ''' selector = Selector(response) urls = selector.xpath('//div[@class="el"]/p/span') for url in urls: url = url.xpath('a/@href').extract()[0] yield Request( url = url, callback = self.parse_job_info, meta = {}, dont_filter = True ) def parse_job_info(self, response): ''' 解析工作详情页 :param response: :return: ''' item = Job51Item() selector = Selector(response) JobTitle = selector.xpath('//div[@class="cn"]/h1/text()').extract()[0].strip().replace(' ','').replace(',',';') CompanyName = selector.xpath('//div[@class="cn"]/p[1]/a[1]/text()').extract()[0].strip().replace(',',';') CompanyNature = selector.xpath('//div[@class="tCompany_sidebar"]/div/div[2]/p[1]/text()').extract()[0].strip().replace(',',';') CompanySize = selector.xpath('//div[@class="tCompany_sidebar"]/div/div[2]/p[2]/text()').extract()[0].strip().replace(',',';') IndustryField = selector.xpath('//div[@class="tCompany_sidebar"]/div/div[2]/p[3]/text()').extract()[0].strip().replace(',',';') Salary = selector.xpath('//div[@class="cn"]/strong/text()').extract()[0].strip().replace(',',';') infos = selector.xpath('//div[@class="cn"]/p[2]/text()').extract() Workplace = infos[0].strip().replace('  ','').replace(',',';') Workyear = infos[1].strip().replace('  ','').replace(',',';') if len(infos) == 4: Education = '' RecruitNumbers = infos[2].strip().replace('  ', '').replace(',',';') ReleaseTime = infos[3].strip().replace('  ', '').replace(',',';') else: Education = infos[2].strip().replace('  ', '').replace(',',';') RecruitNumbers = infos[3].strip().replace('  ', '').replace(',',';') ReleaseTime = infos[4].strip().replace('  ', '').replace(',',';') if len(infos) == 7: Language, Specialty = infos[5].strip().replace('  ',''), infos[6].strip().replace('  ','').replace(',',';') elif len(infos) == 6: if (('英语' in infos[5]) or ('话' in infos[5])): Language, Specialty = infos[5].strip().replace('  ','').replace(',',';'), '' else: Language, Specialty = '', infos[5].strip().replace('  ','').replace(',',';') else: Language, Specialty = '', '' Welfare = selector.xpath('//div[@class="t1"]/span/text()').extract() PositionAdvantage = ';'.join(Welfare).replace(',', ';') item['JobTitle'] =JobTitle item['CompanyName'] =CompanyName item['CompanyNature'] =CompanyNature item['CompanySize'] = CompanySize item['IndustryField'] = IndustryField item['Salary'] =Salary item['Workplace'] = Workplace item['Workyear'] =Workyear item['Education'] =Education item['RecruitNumbers'] = RecruitNumbers item['ReleaseTime'] =ReleaseTime item['Language'] = Language item['Specialty'] = Specialty item['PositionAdvantage'] = PositionAdvantage yield item 复制代码 2.5 保存到csv文件 通过pipelines项目管道保存至csv文件 复制代码 class Job51Pipeline(object): def process_item(self, item, spider): with open(r'D:\Data\ZhaoPin.csv','a', encoding = 'gb18030') as f: job_info = [item['JobTitle'], item['CompanyName'], item['CompanyNature'], item['CompanySize'], item['IndustryField'], item['Salary'], item['Workplace'], item['Workyear'], item['Education'], item['RecruitNumbers'], item['ReleaseTime'],item['Language'],item['Specialty'],item['PositionAdvantage'],'\n'] f.write(",".join(job_info)) return item 复制代码 2.6 配置setting 设置用户代理, 下载延迟0.5s, 关闭cookie追踪, 调用pipelines 复制代码 USER_AGENT = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' DOWNLOAD_DELAY = 0.5 COOKIES_ENABLED = False ITEM_PIPELINES = { 'job51.pipelines.Job51Pipeline': 300, } 复制代码 2.7 运行程序 新建main.py文件, 并执行以下代码 from scrapy import cmdline cmdline.execute('scrapy crawl zhaopin'.split()) 这样开始了数据爬取, 最终爬取到9000多条数据, 在分析这些数据之前, 先看看数据都是什么样, 进入数据概览环节. 3. 数据概览 3.1 读取数据 复制代码 import pandas as pd df = pd.read_csv(r'D:\aPython\Data\DataVisualization\shujufenxishiJob51.csv') #由于原始数据中没有字段, 需要为其添加字段 df.columns = ['JobTitle','CompanyName','CompanyNature','CompanySize','IndustryField','Salary','Workplace','Workyear','Education','RecruitNumbers', 'ReleaseTime','Language','Specialty','PositionAdvantage'] df.info() 复制代码 抛出异常: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbd in position 0: invalid start byte 解决办法; 用Notepad++将编码转换为utf-8 bom格式 转换之后, 再次执行 抛出异常: ValueError: Length mismatch: Expected axis has 15 elements, new values have 14 elements 解决办法: 在列表['JobTitle.....PositionAdvantage']后面追加'NNN', 从而补齐15个元素. 追加之后, 再次执行, 执行结果为: RangeIndex: 9948 entries, 0 to 9947 Data columns (total 15 columns): JobTitle 9948 non-null object CompanyName 9948 non-null object CompanyNature 9948 non-null object CompanySize 9948 non-null object IndustryField 9948 non-null object Salary 9948 non-null object Workplace 9948 non-null object Workyear 9948 non-null object Education 7533 non-null object RecruitNumbers 9948 non-null object ReleaseTime 9948 non-null object Language 901 non-null object Specialty 814 non-null object PositionAdvantage 8288 non-null object NNN 0 non-null float64 dtypes: float64(1), object(14) memory usage: 1.1+ MB 可以了解到的信息: 目前的数据维度9948行X15列, Education, Language, Specialty, PositionAdvantage有不同程度的缺失(NNN是最后添加, 仅仅是用来补齐15元素), 14个python对象(1个浮点型) 3.2 描述性统计 由于我们所需信息的数据类型都是python对象, 故使用以下代码 #注意是大写的字母o df.describe(include=['O']) 从以下信息(公司名称部分我没有截图)中可以得到: 职位名称中'数据分析师'最多, 多为民营公司, 公司规模150-500人最多, 行业领域金融/投资/证券最多, 薪资中6-8千/月最多, 大多对工作经验没有要求, 学历要求多为本科, 多数均招1人等信息. 职位名称的种类就有4758种, 他们都是我们本次分析的数据分析师岗位吗, 先来确认下: zhaopin.JobTitle.unique() array(['零基础免费培训金融外汇数据分析师', '数据分析师(周末双休+上班舒适)', '数据分析师', ..., '数据分析实习(J10635)', '数据分析实习(J10691)', '数据分析实习(J10713)'], dtype=object) 这仅仅显示了职位名称中的一部分,而且还都符合要求, 换种思路先看20个 JobTitle = zhaopin.groupby('JobTitle', as_index=False).count() JobTitle.JobTitle.head(20) 0 (AI)机器学习开发工程师讲师 1 (ID67391)美资公司数据分析 2 (ID67465)美资公司数据分析 3 (ID67674)500强法资汽车制造商数据分析专员(6个月) 4 (ID67897)知名500强法资公司招聘数据分析专员 5 (Senior)DataAnalyst 6 (免费培训)数据分析师+双休+底薪 7 (实习职位)BusinessDataAnalyst/业务数据分析 8 (急)人力销售经理 9 (提供食宿)银行客服+双休 10 (日语)股票数据分析员/EquityDataAnalyst-Japanese/ 11 (越南语)股票数据分析员/EquityDataAnalyst-Vietnam 12 (跨境电商)产品专员/数据分析师 13 (韩语)股票数据分析员/EquityDataAnalyst-Korean 14 ***数据分析 15 -数据分析师助理/实习生 16 -数据分析师助理/统计专员+双休五险+住宿 17 -无销售不加班金融数据分析师月入10k 18 -金融数据分析师助理6k-1.5w 19 -金融数据分析师双休岗位分红 Name: JobTitle, dtype: object 可以看到还有机器学习开发讲师, 人力销售经理, 银行客服等其他无效数据. 现在我们对数据有了大致的认识, 下来我们开始数据预处理. 4. 数据预处理 4.1 数据清洗 数据清洗的目的是不让有错误或有问题的数据进入加工过程, 其主要内容包括: 重复值, 缺失值以及空值的处理 4.1.1 删除重复值 如果数据中存在重复记录, 而且重复数量较多时, 势必会对结果造成影响, 因此我们应当首先处理重复值. #删除数据表中的重复记录, 并将删除后的数据表赋值给zhaopin zhaopin = df.drop_duplicates(inplace = False) zhaopin.shape (8927, 15) 对比之前的数据, 重复记录1021条. 4.1.2 过滤无效数据 我们了解到职位名称中存在无效数据, 我们对其的处理方式是过滤掉. #筛选名称中包含'数据'或'分析'或'Data'的职位 zhaopin = zhaopin[zhaopin.JobTitle.str.contains('.*?数据.*?|.*?分析.*?|.*?Data.*?')] zhaopin.shape (7959, 15) 4.1.3 缺失值处理 在pandas中缺失值为NaN或者NaT, 其处理方式有多种: 1. 利用均值等集中趋势度量填充 2. 利用统计模型计算出的值填充 3. 保留缺失值 4. 删除缺失值 #计算每个特征中缺失值个数 zhaopin.isnull().sum() JobTitle 0 CompanyName 0 CompanyNature 0 CompanySize 0 IndustryField 0 Salary 0 Workplace 0 Workyear 0 Education 1740 RecruitNumbers 0 ReleaseTime 0 Language 7227 Specialty 7244 PositionAdvantage 1364 NNN 7959 dtype: int64 -- Education: 缺失值占比1740/7959 = 21.86%, 缺失很有可能是"不限学历", 我们就用"不限学历"填充 zhaopin.Education.fillna('不限学历', inplace=True) -- Language: 缺失值占比7227/7959 = 90.80%, 缺失太多, 删除特征 -- Specialty: 缺失值占比7244/7959 = 91.02%, 同样缺失很多, 删除 zhaopin.drop(['Specialty','Language'], axis=1, inplace = True) -- PositionAdvantage: 缺失占比1364/7959 = 17.14%, 选用众数中的第一个'五险一金'填充 zhaopin.PositionAdvantage.fillna(zhaopin.PositionAdvantage.mode()[0], inplace = True) -- NNN: 没有任何意义, 直接删除 zhaopin.drop(["NNN"], axis=1, inplace = True) 最后, 检查缺失值是否处理完毕 zhaopin.isnull().sum() JobTitle 0 CompanyName 0 CompanyNature 0 CompanySize 0 IndustryField 0 Salary 0 Workplace 0 Workyear 0 Education 0 RecruitNumbers 0 ReleaseTime 0 PositionAdvantage 0 dtype: int64 4.2 数据加工 由于现有的数据不能满足我们的分析需求, 因此需要对现有数据表进行分列, 计算等等操作. 需要处理的特征有: Salary, Workplace 1. Salary 将薪资分为最高薪资和最低薪资, 另外了解到薪资中单位有元/小时, 元/天, 万/月, 万/年, 千/月, 统一将其转化为千/月 复制代码 import re #将5种单元进行编号 zhaopin['Standard'] = np.where(zhaopin.Salary.str.contains('元.*?小时'), 0, np.where(zhaopin.Salary.str.contains('元.*?天'), 1, np.where(zhaopin.Salary.str.contains('千.*?月'), 2, np.where(zhaopin.Salary.str.contains('万.*?月'), 3, 4)))) #用'-'将Salary分割为LowSalary和HighSalary SalarySplit = zhaopin.Salary.str.split('-', expand = True) zhaopin['LowSalary'], zhaopin['HighSalary'] = SalarySplit[0], SalarySplit[1] #Salary中包含'以上', '以下'或者两者都不包含的进行编号 zhaopin['HighOrLow'] = np.where(zhaopin.LowSalary.str.contains('以.*?下'), 0, np.where(zhaopin.LowSalary.str.contains('以.*?上'), 2, 1)) #匹配LowSalary中的数字, 并转为浮点型 Lower = zhaopin.LowSalary.apply(lambda x: re.search('(\d+\.?\d*)', x).group(1)).astype(float) #对LowSalary中HighOrLow为1的部分进行单位换算, 全部转为'千/月' zhaopin.LowSalary = np.where(((zhaopin.Standard==0)&(zhaopin.HighOrLow==1)), Lower*8*21/1000, np.where(((zhaopin.Standard==1)&(zhaopin.HighOrLow==1)), Lower*21/1000, np.where(((zhaopin.Standard==2)&(zhaopin.HighOrLow==1)), Lower, np.where(((zhaopin.Standard==3)&(zhaopin.HighOrLow==1)), Lower*10, np.where(((zhaopin.Standard==4)&(zhaopin.HighOrLow==1)), Lower/12*10, Lower))))) #对HighSalary中的缺失值进行填充, 可以有效避免匹配出错. zhaopin.HighSalary.fillna('0千/月', inplace =True) #匹配HighSalary中的数字, 并转为浮点型 Higher = zhaopin.HighSalary.apply(lambda x: re.search('(\d+\.?\d*).*?', str(x)).group
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信