在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°reefrom=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)
输出结果:
这时候说明百度还是很重要的
发现除了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=area
data_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"%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)
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=area
data_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)