使用Python爬取前程无忧上南京地区Python职位以及对应工资

2023-10-21 23:59

本文主要是介绍使用Python爬取前程无忧上南京地区Python职位以及对应工资,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

获取原始数据

最近在学习Python,做了一个爬虫程序练练手,前程无忧这个网站页面布局还是挺简单的,适合我这种新手。使用requests+bs4爬取
不多说了,先来看看页面布局吧。

这是前程无忧上的职位列表,看上去还是很清楚的
这是前程无忧上的职位列表,看上去还是很清楚的

然后再来看看页面布局,使用Google浏览器打开前程无忧网页,然后按下F12

每一个class为el的div就代表一个招聘信息
每一个class为el的div就代表一个招聘信息

然后再来看看div里面是怎么布局的,我们需要获取第二列公司名称以及第四列的薪资,其他的暂时不管。

公司名称在el这个div下面的class为t2的span标签下面。


薪资在t4的span标签下面。
这样获取的逻辑就很简单了。

  • 首先使用BeautifulSoup解析request获取的html
  • 找到所有class=el的div标签的集合
  • 在每个el标签下找到t2和t4标签,将它们的内容读取保存在字典中
  • 比如:‘南京万瑞建设工程有限公司’:‘12-20万/年’

以上是爬取一页的数据,我们当然不能只能爬取一页这么简单了,往下一页爬取有两种方法:
第一种方法就是获取到下一页的url,然后通过递归调用,不断地爬取
下一页的标签是这样的:

下一页在class等于bk的li标签下的a标签里面,首先要获取下一页的标签next_page,然后通过

next_url = next_page['href']

就可以获取到url,然后重复上述爬取一页的步骤,就可以不断的往下爬取了。

第二种方法我们来研究一下url
https://search.51job.com/list/070200,000000,0000,00,9,99,Python,2,1.html
这是第一页的url,注意最后的Python,2,1
https://search.51job.com/list/070200,000000,0000,00,9,99,Python,2,2.html
这是第二页,区别就是2,1变成了2,2
后面第三页,第四页就不看了,都是一样,只有数字的变化,这样就可以采用for循环了。

urlpri = 'https://search.51job.com/list/070200,000000,0000,00,9,99,Python,2,{page}.html'
for i in range(1,page+1) :#从1开始,并且page要加1,不然爬取不到最后一页url = urlpri.format(**{'page':i})

下面来看这部分代码:

def salary_python(page):"""获取前程无忧上前page页的Python职位对应的公司名称以及薪水:param page: 页数:return:返回公司名称与薪水对应的字典"""urlpri = 'https://search.51job.com/list/070200,000000,0000,00,9,99,Python,2,{page}.html'info = {}for i in range(1,page+1):url = urlpri.format(**{'page':i})res = requests.get(url)if res.status_code == 200:soup = BeautifulSoup(res.content, 'lxml')jobList = soup.find_all('div', class_='el')for job in jobList:companyName = ''salary = ''companyTag = job.find('span', class_='t2')if companyTag:companyTag1 = companyTag.find('a')if companyTag1:companyName = companyTag1.stringsalaryTag = job.find('span', class_='t4')if salaryTag:salary = salaryTag.stringif companyName and salary: #有些职位没有写薪资,遇到这样的我们就跳过,这地方需要过滤一下info[companyName] = salaryreturn info

这是运行结果
看一下运行结果,是可以获取到对应的公司名称以及开出的薪资的,我的pycharm不知道出了啥问题,debug的时候想点开看一下,结果一点开debug就卡死,有知道原因的大神可以来解答一下。

数据简单处理

上面我们获取到的是原始数据,只有这个数据是看不出什么的,所以我们简单处理一下,这里用到Python的pandas模块,需要先安装一下

pip install pandas

主要是对工资进行处理,研究一下发现工资的写法主要包括下面几种

  • xx(-xx)万/月
  • xx(-xx)万/年
  • xx(-xx)千/月
  • xx(-xx)百/天
    或者没有万,百,千这种单位,直接是数字加上时间(月,年,天)
    剩下一些少见的单位,像什么万/天、百/月,就不考虑了。

我们单独写一个方法来处理工资的这种表述,统一化成 以月为单位 的形式
比如12万/年,将其转化为10000
对于工资在某一范围的,就取个中间值吧
比如0.6–1.2万/月,转化为9000

下面来看代码:

def handle_salary(s):"""将str类型的薪水表述,转化为flaot类型,并全部转化为 元/月为单位:param s: 字符串类型数据:return: float类型薪水"""if r'万' in s and r'月' in s:matchwMouth1 = re.match(r'(\d+\.*\d*)\-(\d+\.*\d*)', s, re.I)matchwMouth2 = re.match(r'(\d+\.*\d*)', s, re.I)if matchwMouth1:sMin = float(matchwMouth1.group(1))sMax = float(matchwMouth1.group(2))salary = (sMax+sMin)/2.0return round(salary*10000, 2)elif matchwMouth2:salary = float(matchwMouth2.group(1))return round(salary*10000, 2)else:return selif r'万' in s and r'年' in s:matchwYear1 = re.match(r'(\d+\.*\d*)\-(\d+\.*\d*)', s, re.I)matchwYear2 = re.match(r'(\d+\.*\d*)', s, re.I)if matchwYear1:sMin = float(matchwYear1.group(1))sMax = float(matchwYear1.group(2))salary = (sMax + sMin) / (2.0*12)return round(salary*10000, 2)elif matchwYear2:salary = float(matchwYear2.group(1))/12.0return round(salary*10000, 2)else:return selif r'千' in s and r'月' in s:matchqMouth1 = re.match(r'(\d+\.*\d*)\-(\d+\.*\d*)', s, re.I)matchqMouth2 = re.match(r'(\d+\.*\d*)', s, re.I)if matchqMouth1:sMin = float(matchqMouth1.group(1))sMax = float(matchqMouth1.group(2))salary = (sMax + sMin) / 2.0return round(salary*1000, 2)elif matchqMouth2:salary = float(matchqMouth2.group(1))return round(salary*1000, 2)else:return selif r'百' in s and r'天' in s:matchbDay1 = re.match(r'(\d+\.*\d*)\-(\d+\.*\d*)', s, re.I)matchbDay2 = re.match(r'(\d+\.*\d*)', s, re.I)if matchbDay1:sMin = float(matchbDay1.group(1))sMax = float(matchbDay1.group(2))salary = (sMax + sMin) / 2.0return round(salary*3000, 2)elif matchbDay2:salary = float(matchbDay2.group(1))return round(salary*3000, 2)else:return selif r'天' in s:matchbDay1 = re.match(r'(\d+\.*\d*)\-(\d+\.*\d*)', s, re.I)matchbDay2 = re.match(r'(\d+\.*\d*)', s, re.I)if matchbDay1:sMin = float(matchbDay1.group(1))sMax = float(matchbDay1.group(2))salary = (sMax + sMin) / 2.0return round(salary*30, 2)elif matchbDay2:salary = float(matchbDay2.group(1))return round(salary*30, 2)else:return selif r'月' in s:matchMouth1 = re.match(r'(\d+\.*\d*)\-(\d+\.*\d*)', s, re.I)matchMouth2 = re.match(r'(\d+\.*\d*)', s, re.I)if matchMouth1:sMin = float(matchMouth1.group(1))sMax = float(matchMouth1.group(2))salary = (sMax + sMin) / 2.0return round(salary, 2)elif matchMouth2:salary = float(matchMouth2.group(1))return round(salary, 2)else:return selif r'年' in s:matchYear1 = re.match(r'(\d+\.*\d*)\-(\d+\.*\d*)', s, re.I)matchYear2 = re.match(r'(\d+\.*\d*)', s, re.I)if matchYear1:sMin = float(matchYear1.group(1))sMax = float(matchYear1.group(2))salary = (sMax + sMin) / (2.0 * 12)return round(salary, 2)elif matchYear2:salary = float(matchYear2.group(1)) / 12.0return round(salary, 2)else:return selse:return s  #工资的表示形式如果不在上述几种形式范围内,就返回它的原有值,这个到后面还要在处理

这里的输出都是正确的
调试一下,输出都是对的。

接下来用一个for循环,把全部数据处理一下。
奉上代码:

def cleanData(dict):"""清理获取的数据,薪水全部转化为float,单位为 元/月:param dict: 获取的原始数据:return: 返回处理过的数据"""nameList = list(dict.keys())salary = list(dict.values())salaryList = []for item in salary:salaryItem = handle_salary(item)salaryList.append(salaryItem)dataDict = {}for i in range(len(nameList)):dataDict[nameList[i]] = salaryList[i]dataDictNew = {}for key,value in dataDict.items():if isinstance(value,float):dataDictNew[key] = valuedata = {'name': list(dataDictNew.keys()), 'salary': list(dataDictNew.values())}df = pd.DataFrame(data)return df

这里有几点要说明

    dataDict = {}for i in range(len(nameList)):dataDict[nameList[i]] = salaryList[i]

对数据的处理其实到这儿就已经结束了

 dataDictNew = {}for key,value in dataDict.items():if isinstance(value,float):dataDictNew[key] = value

这部分代码是为了过滤掉上面说的handle_salary这个方法没能处理掉的数据,经过处理过的工资,数据类型应该都是float,如果不是float,就将这条数据删除。

    data = {'name': list(dataDictNew.keys()), 'salary': list(dataDictNew.values())}df = pd.DataFrame(data)return df

这是将数据保存为pandas的dataframe形式,关于这种数据结构,可以参考这篇博客
https://www.cnblogs.com/IvyWong/p/9203981.html

保存数据

经过处理过的数据,将它保存在一个csv文件里,方便以后查看,pandas模块有直接处理csv文件的方法,非常方便。

def saveDataCsv(df):"""保存数据到csv文件中:param df: dataframe类型数据:return:"""filename = 'python_51job_salary.csv'try:df.to_csv(filename, encoding='gbk')except UnicodeEncodeError:print("编码错误, 该数据无法写到文件中, 直接忽略该数据")

这是保存的csv文件

简单分析

dataframe有个describe()方法,可以简单分析一下数据,我们来看一下

print(dfSalary.describe())  #dfsalary是处理过的数据,保存为dataframe格式

输出:

             salary
count    146.000000       #样本数量
mean   13519.748836     #工资平均值
std     7021.955639         #工资标准差
min     3750.000000        #最小值
25%     9000.000000		#从小到大排列,第25%的那个数
50%    12500.000000      #中位数
75%    15750.000000      #同25%
max    45000.000000       #最大值

还可以利用pandas模块画一个折线图

dfSalary.plot()
plt.show()  #绘制折现图表


工资看上去一万到两万的比较多。
下面这个方法用于统计工资高于某个值所占的比例:

def over(df, num):"""计算高于num的工资所占比例:param df: 原始数据,dataframe类型:param num: 高于工资:return: 比例"""salaryList = df.salary.tolist()overList = [x for x in salaryList if x>num]over = len(overList)*1.0/len(salaryList)return over
print(over(dfSalary,10000))    #工资高于10000所占比例  输出:0.7123287671232876

各位可以看下自己大概在啥水平哈。

新手一枚,代码还有许多不合理之处,欢迎各位大神提出意见,共同进步!!
附上github源码:
https://github.com/cchhgithub/pythonLearning/blob/master/pachong/51job.py

这篇关于使用Python爬取前程无忧上南京地区Python职位以及对应工资的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/257773

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss