抓取前程无忧51job岗位数据,实现数据可视化——心得体会

本文主要是介绍抓取前程无忧51job岗位数据,实现数据可视化——心得体会,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近找工作,经常浏览51job,刚好学了python一段时间了,所以有了一个想法:为什么不将我需要的岗位信息给爬出来呢?

在51job网站搜索“数据分析师”,查看源代码,发现每一个招聘公告包含岗位、公司、薪资、地区等信息。所以可以实现如下几个目的:

1.根据关键词抓取招聘信息;

2.连接mysql,创建表格,并插入数据;

3.初步清洗数据,实现可视化

一、网页抓取函数

https://search.51job.com/list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588,2,1.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=

这个是搜索“数据分析师”的网址,乍一看十分复杂,立马就没弄下去的欲望了。

发现这么一长串url只有

https://search.51job.com/list/000000,000000,0000,00,9,99,%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588,2,1.html

是必须的。而

%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588

则正好是“数据分析师”的urlencode,那么我们可以利用这一点,更改搜索关键词

而且%25E6%2595%25B0%25E6%258D%25AE%25E5%2588%2586%25E6%259E%2590%25E5%25B8%2588,2,1.html中html前的数字对应着页码

因此可以通过更改keyword和页码,进一步选择我们需要的信息。

def Get_html(data,i):Url="https://search.51job.com/list/000000,000000,0000,00,9,99,"+data+",2,%s.html"%i #data是自定义搜索关键词,i的大小则直接控制了爬取信息量header = {'User-Agent':'Mozilla/5.0'} #头部信息r=requests.get(Url,headers=header)print(r.url)print(r.status_code)r.encoding=r.apparent_encoding#print(r.text)return(r.text) #函数返回html

万里长征完成第一步o.o

接下来就是解析html,进一步爬取岗位、公司、薪资、地区和url信息。

这个阶段卡了2个多小时(习惯用beautifulsoup爬取)。

代码1:

def Get_data(html):
    soup=BeautifulSoup(html,"html.parser")
    for div in soup.find_all("div",class_="el"):  #find_all()遍历结果为列表
        print(div)

输出结果:

<div class="el">
<p class="t1 ">
<em class="check" name="delivery_em" οnclick="checkboxClick(this)"></em>
<input class="checkbox" jt="0" name="delivery_jobid" style="display:none" type="checkbox" value="109229565"/>
<span>
<a href="https://jobs.51job.com/shenyang/109229565.html?s=01&amp;t=0" οnmοusedοwn="" target="_blank" title="数据分析师">
                                    数据分析师                                </a>
</span>
</p>
<span class="t2"><a href="https://jobs.51job.com/all/co5314555.html" target="_blank" title="沈阳市和平区鹏润衣世界服装批发城">沈阳市和平区鹏润衣世界服装批发城</a></span>
<span class="t3">沈阳</span>
<span class="t4">6-8千/月</span>
<span class="t5">03-19</span>
</div>
发现再怎么利用beautifulsoup的函数也无法对div子节点的属性进行爬取。(习惯了用beautifulsoup,一下子不知道怎么办了)

这时候说明百度还是很重要的

发现除了beautifulsoup库外,xpath也可以实现这个功能。(详细功能自行百度)

代码2:

def Get_data(html,name):html = etree.HTML(html)divs = html.xpath("//div[@id='resultList']/div[@class='el']")print(divs)for div in divs:job1=div.xpath("./p/span/a/@title")print(job1[0])job_url1=div.xpath("./p/span/a/@href")print(job_url1)job_company1=div.xpath("./span[@class='t2']/a/@title")print(job_company1)job_area1=div.xpath("./span[@class='t3']/text()")print(job_area1)job_salary1=div.xpath("./span[@class='t4']/text()")print(job_salary1)try:job_salary.append(job_salary1[0])job_area.append(job_area1[0])shuju.append((job1[0], job_url1[0], job_company1[0], job_area1[0], job_salary1[0]))except:print("异常")print(shuju)print(job_salary,job_area)print(len(job_salary),len(job_area)) #查看招聘信息数量return(shuju,job_salary,job_area)

二、连接mysql数据库,创建表格,插入数据

利用pymysql库

代码1(创建表格函数):

def Mysql_create_table(name):client=pymysql.connect(user="root",host="localhost",passwd="*******",db="xiaolimao")cursor=client.cursor()sql="create table if not exists table_%s"%name+"(job VARCHAR(100),job_url VARCHAR(100),job_company VARCHAR(100),job_area VARCHAR(100),job_salary VARCHAR(100));"cursor.execute(sql)cursor.close()client.close()

写入爬取数据:

代码2:

def Mysql_data(name,shuju):client=pymysql.connect(user="root",host="localhost",passwd="********",db="xiaolimao")cursor=client.cursor()sql="insert into table_%s"%name+" values(%s,%s,%s,%s,%s)"cursor.executemany(sql,shuju)client.commit()cursor.close()client.close()

处理结果:

三、初步筛选数据,做工资分布图(地点-薪资)

51job爬出的薪资数据均为string格式,且为1-2万/月或者1-2千/月格式。地点为城市或者城市-区格式

首先将工资数据进行处理:

def Num_salary():for num in job_salary: #job_salary 为工资列表if("万/月" in num ): Num=(re.findall(r"\d+\.?\d*",num))  #将工资字符串中数字提取出n=int((float(Num[0])+float(Num[1]))*10000/2)  #将工资区间平均化elif ("千/月" in num ):Num = (re.findall(r"\d+\.?\d*", num))n = int((float(Num[0]) + float(Num[1])) * 1000 / 2)data_salary.append(n) # 新的工资列表print(data_salary)return (data_salary)

然后城市处理:

def Num_area():for area in job_area:if "-" in area:Area = area.split("-")[0] #将带有区的地点只取城市else :Area=areadata_area.append(Area)print(data_area)print(len(data_area))return (data_area)

处理结果:

[20000, 9000, 6500, 6500, 15500, 9000, 7000, 5250, 7000, 9000, 12500, 7000, 9000, 7000, 5250, 10000, 9000, 8500, 7000, 6500, 7000, 9000, 18000, 9000, 7000, 6500, 12000, 12000, 12500, 11500, 11500, 8500, 5250, 7000, 7000, 7000, 6000, 12500, 7000, 5250, 5250, 9500, 9000, 9000, 7000, 6250, 6500, 7500, 4500, 10000, 7000, 15000, 14500, 8000, 25000, 7000, 12500, 27500, 9000, 25000, 15000, 8500, 7000, 5500, 24000, 10000, 10000, 6000, 16000, 11500, 12500, 12500, 17500, 7500, 11500, 9000, 7500, 12500, 7000, 7000, 11500, 11500, 12500, 6000, 12500, 5250, 11500, 7000, 12000, 9000, 12500, 10500, 12500, 7000, 6750, 6500, 9000, 9000, 10000, 5250, 5000, 5250, 7250, 9000, 7000, 6000, 9000, 9000, 9000, 9000, 7000, 12500, 4150, 6000, 5250, 9000, 17500, 20000, 20000, 5500, 8500, 4500, 7500, 8500, 6000, 5750, 22500, 11500, 11500, 5250, 12500, 7500, 7500, 5000, 17500, 9000, 19000, 15000, 12500, 5500, 11500, 11500, 6000, 6750, 11500, 12500, 12500]

['杭州', '无锡', '深圳', '异地招聘', '异地招聘', '无锡', '广州', '广州', '广州', '广州', '北京', '广州', '北京', '广州', '广州', '深圳', '深圳', '广州', '广州', '上海', '广州', '南昌', '深圳', '广州', '成都', '上海', '上海', '广州', '广州', '北京', '广州', '上海', '成都', '广州', '深圳', '上海', '合肥', '上海', '武汉', '郑州', '郑州', '广州', '上海', '广州', '杭州', '佛山', '中山', '海口', '异地招聘', '深圳', '沈阳', '上海', '异地招聘', '重庆', '上海', '北京', '北京', '珠海', '上海', '上海', '广州', '上海', '西安', '上海', '深圳', '深圳', '北京', '异地招聘', '南京', '深圳', '上海', '广州', '成都', '金华', '广州', '北京', '广州', '西安', '青岛', '异地招聘', '广州', '广州', '上海', '北京', '深圳', '沈阳', '成都', '大连', '武汉', '武汉', '南京', '武汉', '上海', '广州', '上海', '西安', '广州', '宜昌', '上海', '广州', '长沙', '昆明', '杭州', '福州', '广州', '佛山', '广州', '北京', '北京', '深圳', '广州', '深圳', '苏州', '温州', '异地招聘', '广州', '武汉', '北京', '广州', '上海', '广州', '深圳', '广州', '上海', '郑州', '郑州', '广州', '深圳', '上海', '广州', '上海', '广州', '深圳', '广州', '上海', '深圳', '北京', '深圳', '杭州', '广州', '上海', '上海', '合肥', '深圳', '上海', '北京', '杭州']

然后发现,在城市列表中存在“异地招聘”非城市名字符串。将城市列表中“异地招聘”元素删除并将其对应的工资列表中的元素删除。

def Get_rid(i):for j in range(i):  # 列表在删除元素时因为地址的更变会导致部分元素删除不彻底,此处加一个循环为了更彻底,i与爬取信息量有关for area in data_area :if area == "异地招聘" :data_salary.pop(data_area.index(area))data_area.remove(area)

处理结果:

[20000, 9000, 6500, 9000, 7000, 5250, 7000, 9000, 12500, 7000, 9000, 7000, 5250, 10000, 9000, 8500, 7000, 6500, 7000, 9000, 18000, 9000, 7000, 6500, 12000, 12000, 12500, 11500, 11500, 8500, 5250, 7000, 7000, 7000, 6000, 12500, 7000, 5250, 5250, 9500, 9000, 9000, 7000, 6250, 6500, 7500, 10000, 7000, 15000, 8000, 25000, 7000, 12500, 27500, 9000, 25000, 15000, 8500, 7000, 5500, 24000, 10000, 10000, 16000, 11500, 12500, 12500, 17500, 7500, 11500, 9000, 7500, 12500, 7000, 11500, 11500, 12500, 6000, 12500, 5250, 11500, 7000, 12000, 9000, 12500, 10500, 12500, 7000, 6750, 6500, 9000, 9000, 10000, 5250, 5000, 5250, 7250, 9000, 7000, 6000, 9000, 9000, 9000, 9000, 7000, 12500, 4150, 6000, 9000, 17500, 20000, 20000, 5500, 8500, 4500, 7500, 8500, 6000, 5750, 22500, 11500, 11500, 5250, 12500, 7500, 7500, 5000, 17500, 9000, 19000, 15000, 12500, 5500, 11500, 11500, 6000, 6750, 11500, 12500, 12500] ['杭州', '无锡', '深圳', '无锡', '广州', '广州', '广州', '广州', '北京', '广州', '北京', '广州', '广州', '深圳', '深圳', '广州', '广州', '上海', '广州', '南昌', '深圳', '广州', '成都', '上海', '上海', '广州', '广州', '北京', '广州', '上海', '成都', '广州', '深圳', '上海', '合肥', '上海', '武汉', '郑州', '郑州', '广州', '上海', '广州', '杭州', '佛山', '中山', '海口', '深圳', '沈阳', '上海', '重庆', '上海', '北京', '北京', '珠海', '上海', '上海', '广州', '上海', '西安', '上海', '深圳', '深圳', '北京', '南京', '深圳', '上海', '广州', '成都', '金华', '广州', '北京', '广州', '西安', '青岛', '广州', '广州', '上海', '北京', '深圳', '沈阳', '成都', '大连', '武汉', '武汉', '南京', '武汉', '上海', '广州', '上海', '西安', '广州', '宜昌', '上海', '广州', '长沙', '昆明', '杭州', '福州', '广州', '佛山', '广州', '北京', '北京', '深圳', '广州', '深圳', '苏州', '温州', '广州', '武汉', '北京', '广州', '上海', '广州', '深圳', '广州', '上海', '郑州', '郑州', '广州', '深圳', '上海', '广州', '上海', '广州', '深圳', '广州', '上海', '深圳', '北京', '深圳', '杭州', '广州', '上海', '上海', '合肥', '深圳', '上海', '北京', '杭州']

非城市名字符串全部删除

最后成图:

def Geo_chart(name): #利用pyecharts库的Geo函数做图data_geo=[]data_salary_max=np.max(data_salary) #利用工资列表最大值做为比例尺最大值print(data_salary_max)for i in range(len(data_area)-1):data_geo.append((data_area[i],data_salary[i]))geo=Geo("%s"%name+"薪资分布图","data from 51job", title_color="#fff", title_pos="center",width=1200, height=600, background_color='#404a59')attr,value=geo.cast(data_geo)geo.add("", attr, value, visual_range=[0, data_salary_max], visual_text_color="#fff", symbol_size=15, is_visualmap=True)geo.show_config()geo.render() #可以自定义保存位置

成果图:

大功告成!!!

总结:

从开始做一直到成功,花了接近一天时间。中间多次想要放弃,不过好在是坚持了下来。

好多东西真的是在自己动手做的时候才发现没有想象中的简单。

参考文章:

1.https://blog.csdn.net/legalhighhigh/article/details/79779832

2.

 

贴上完整代码:

import requests
import re
from lxml import etree
import pymysql
from pyecharts import Geo
import numpy as np
job_salary=[]
job_area=[]
shuju=[]
data_salary=[]
data_area=[]def Geo_chart(name):data_geo=[]data_salary_max=np.max(data_salary)print(data_salary_max)for i in range(len(data_area)-1):data_geo.append((data_area[i],data_salary[i]))geo=Geo("%s"%name+"薪资分布图","data from 51job", title_color="#fff", title_pos="center",width=1200, height=600, background_color='#404a59')attr,value=geo.cast(data_geo)geo.add("", attr, value, visual_range=[0, data_salary_max], visual_text_color="#fff", symbol_size=15, is_visualmap=True)geo.show_config()geo.render()def Get_html(data,i):Url="https://search.51job.com/list/000000,000000,0000,00,9,99,"+data+",2,%s.html"%iheader = {'User-Agent':'Mozilla/5.0'}r=requests.get(Url,headers=header)print(r.url)print(r.status_code)r.encoding=r.apparent_encoding#print(r.text)return(r.text)    
def Mysql_create_table(name):client=pymysql.connect(user="root",host="localhost",passwd="rongchao123",db="xiaolimao")cursor=client.cursor()sql="create table if not exists table_%s"%name+"(job VARCHAR(100),job_url VARCHAR(100),job_company VARCHAR(100),job_area VARCHAR(100),job_salary VARCHAR(100));"cursor.execute(sql)cursor.close()client.close()def Mysql_data(name,shuju):client=pymysql.connect(user="root",host="localhost",passwd="rongchao123",db="xiaolimao")cursor=client.cursor()sql="insert into table_%s"%name+" values(%s,%s,%s,%s,%s)"cursor.executemany(sql,shuju)client.commit()cursor.close()client.close()def Get_data(html,name):html = etree.HTML(html)divs = html.xpath("//div[@id='resultList']/div[@class='el']")print(divs)for div in divs:job1=div.xpath("./p/span/a/@title")print(job1[0])job_url1=div.xpath("./p/span/a/@href")print(job_url1)job_company1=div.xpath("./span[@class='t2']/a/@title")print(job_company1)job_area1=div.xpath("./span[@class='t3']/text()")print(job_area1)job_salary1=div.xpath("./span[@class='t4']/text()")print(job_salary1)try:job_salary.append(job_salary1[0])job_area.append(job_area1[0])shuju.append((job1[0], job_url1[0], job_company1[0], job_area1[0], job_salary1[0]))except:print("异常")print(shuju)print(job_salary,job_area)print(len(job_salary),len(job_area))return(shuju,job_salary,job_area)def Num_salary():for num in job_salary: #job_salary 为工资列表if("万/月" in num ):Num=(re.findall(r"\d+\.?\d*",num))  #将工资字符串中数字提取出n=int((float(Num[0])+float(Num[1]))*10000/2)  #将工资区间平均化elif ("千/月" in num ):Num = (re.findall(r"\d+\.?\d*", num))n = int((float(Num[0]) + float(Num[1])) * 1000 / 2)data_salary.append(n) # 新的工资列表print(data_salary)return (data_salary)def Num_area():for area in job_area:if "-" in area:Area = area.split("-")[0] #将带有区的地点只取城市else :Area=areadata_area.append(Area)print(data_area)print(len(data_area))return (data_area)def Get_rid(i):for j in range(i):  # 列表在删除元素时因为地址的更变会导致部分元素删除不彻底,此处加一个循环为了更彻底,i与爬取信息量有关for area in data_area :if area == "异地招聘" :data_salary.pop(data_area.index(area))data_area.remove(area)            if __name__=="__main__":kywd=input("关键词:")name=input("表名:")Number=int(input("请输入一个整数:"))Mysql_create_table(name)for i in range(1,Number):html=Get_html(kywd,i)Get_data(html,name)Mysql_data(name,shuju)Num_salary()Num_area()Get_rid(Number)print(data_salary,data_area)Geo_chart(kywd)

这篇关于抓取前程无忧51job岗位数据,实现数据可视化——心得体会的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

使用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

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time