- 前言
- 一、数据爬取
- 1.数据爬取
- 1.1主函数处理
- 1.2网页提取
- 1.3相关信息提取
- 1.4数据保存
- 二、数据可视化
- 1.前端布局
- 2.图表设计
- 2.1 学历与岗位
- 2.1.1数据获取
- 2.1.2 ajax数据交互
- 2.1.3图形绘制
- 2.2中国地图绘制
- 2.3滚动字幕设计
- 总结
前言
戳这里看项目展示
采用工具:python,echarts,mysql,flask。
本文适合初学者使用,但是要具备一定的python、前端、数据库知识。
- 建立while循环可以使程序多次运行,可以在我们的控制下停止。
- 输入work,nums关键字,其中work是我们需要爬取岗位的关键字,nums是待爬取的页数。
- 建立for page in range(1, nums + 1)循环,对翻页进行处理。
- 将将work,nums添加到url中。
while True:
key = int(input("请输入0或1,1开始执行,0退出程序:"))
if key == 1:
work = input("请输入你要爬取的岗位:")
nums = int(input("请输入要爬取的页数:"))
for page in range(1, nums + 1): # 翻页处理,因为待爬取网站url是从1开始,所以前面是1,又因循环是前闭后开所以num要加1
url = "https://search.51job.com/list/000000,000000,0000,00,9,99," + work + ",2,{}.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=".format(
page)
print(url) # 打印出我们要爬取的url
html = getHTMLText(url)
mydb_all(job(html))
else:
print('bye!')
break
1.2网页提取
def getHTMLText(url):
try:
headers = {
"User-Agent": "" ,
'Cookie': "" }
r = requests.get(url, headers=headers, timeout=40)
#print(r.text)
return r.text
except:
return ""
1.3相关信息提取
这是前面爬取的信息,同时我们需要的信息在“nwindow.SEARCH_RESULT =”中,因此采用正则方式对其进行提取。
r = re.findall('window.__SEARCH_RESULT__ = (.*?)</script>', html, re.S) # 提取所需要的信息
对所得数据进行拼接和数据转换
string = ''.join(r) # 进行拼接
infodict = json.loads(string) # 将格式转化为json
进一步对信息进行详细提取;
result = infodict['engine_jds'] # 提取engine_jds标签下的内容
#print(result)
data_dict = []
data_area=[]
for i in result: # 建立循环对数据进行进一步提取
各个标签信息提取:
r = re.findall('window.__SEARCH_RESULT__ = (.*?)</script>', html, re.S) # 提取所需要的信息
string = ''.join(r) # 进行拼接
infodict = json.loads(string) # 将格式转化为json
result = infodict['engine_jds'] # 提取engine_jds标签下的内容
#print(result)
data_dict = []
data_area=[]
for i in result: # 建立循环对数据进行进一步提取ti
ti = i['issuedate'][:10]
job_name = i['job_name'] # 工作名称
job_name = re.sub(u"(.*?)|\(.*?\)", "", job_name) # 消除工作名括号
company_name = i['company_name'] # 公司名称
companytype_text = i['companytype_text'] # 公司类型(民营,合资,上市。。。)
companytype_text = re.sub(u"(.*?)|\(.*?\)", "", companytype_text) #消除括号
if companytype_text == "创业公司":
companytype_text = "民营公司"
elif companytype_text == "外企代表处":
companytype_text = "外资"
elif companytype_text == "合资":
companytype_text = "外资"
elif companytype_text == "非营利组织":
companytype_text = "民营公司"
money = i['providesalary_text'] # 薪资标准
if money:
if str(money) == 'None':
pass
elif money[-3:] == '万/月':
money = money.replace('万/月', '-万/月')
x =money.split('-')
money = (float(x[0]) + float(x[1])) * 10000 / 2
elif money[-3:] == '千/月':
money = money.replace('千/月', '-千/月')
x = money.split('-')
money = (float(x[0]) + float(x[1])) * 1000 / 2
elif money[-3:] == '万/年':
money = money.replace('万/年', '-万/年')
x = money.split('-')
money = (float(x[0]) + float(x[1])) / 2 * 10000 / 12
else:
break
money = ('%.2f' % money)
leve = '' # 给工资分等级
if money:
if 0 < float(money) < 10000:
leve = 'level_1'
elif 10000<= float(money) < 15000:
leve = 'level_2'
elif 15000 <= float(money) < 20000:
leve = 'level_3'
elif 20000 <= float(money) < 25000:
leve = 'level_4'
elif 25000 <= float(money):
leve = 'level_5'
attribute = i['attribute_text'][2] # 学历要求
if attribute:
if attribute == "本科":
attribute = attribute
elif attribute == "大专":
attribute = attribute
elif attribute == "硕士":
attribute = attribute
elif attribute == "博士":
attribute = attribute
else:
break
workexperience = i['attribute_text'][1]
jobwelf = i['jobwelf'] # 公司福利
workarea_text = i['workarea_text'].split('-')[0] # 工作地点
1.4数据保存
data_all.append([job_name,ti, company_name, area, companytype_text, money,leve, attribute, workexperience, jobwelf])
cxn = pymysql.connect(host="127.0.0.1",
user="root",
password="123",
db="51job",
port=3309, # 端口号因为我的3306端口占用因此使用3309端口
charset="utf8")
cur = cxn.cursor()
value = data_dict
sql = "insert into job(job,ti,company,country,companytype,money,leve,attribut,workexperience,jobwelf) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
for v in value:
cur.execute(sql,v)
cxn.commit()
到这里爬虫篇结束!!1
二、数据可视化 1.前端布局我们将一张整张大屏分成了6个区域:可以点这里去查看
<div id="title">51job工作分析</div>
<div id="l1">我是左1</div>
<div id="l2">我是左2</div>
<div id="c1">我是中1</div>
<div id="c2">我是中2</div>
<div id="r1">我是右1</div>
<div id="r2">我是右2</div>
2.图表设计
2.1 学历与岗位
2.1.1数据获取
此处展示学历与岗位之间的信息
- 首先从数据库中提取相应的数据
sql = "select attribut, count(country) as area from job GROUP BY attribut "
- 然后将数据进行处理,得到我们所需要的数据格式,同时将数据格式转化为josn格式。
data = utils.get_l1_data()
edu= []
num = []
for k, v in data:
edu.append(k)
num.append(int(v))
return jsonify({"edu": edu, "num": num})
2.1.2 ajax数据交互
- 之前在项目中用到ajax,是前端的技术,但是感觉作为后端开发者,对它的掌握还是很有必要的。
- ajax全称为Asynchronous JavaScript And XML,就是异步的JS和XML。特点是可以在浏览器中向服务器发送异步请求,最大的优势就是无刷新获取数据。
什么是同步请求?(false)
同步请求是指当前发出请求后,浏览器什么都不能做,
必须得等到请求完成返回数据之后,才会执行后续的代码,
相当于生活中的排队,必须等待前一个人完成自己的事物,后一个人才能接着办。
也就是说,当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面处于一个假死状态,``
什么是异步请求?(默认:true)
默认异步:异步请求就当发出请求的同时,浏览器可以继续做任何事,
Ajax发送请求并不会影响页面的加载与用户的 *** 作,相当于是在两条线上,各走各的,互不影响。
一般默认值为true,异步。异步请求可以完全不影响用户的体验效果,
无论请求的时间长或者短,用户都在专心的 *** 作页面的其他内容,并不会有等待的感觉。
function get_l1_data() {
$.ajax({
url: "/l1",
success: function (data) {
ec_left1_option.xAxis.data=data.edu;
ec_left1_option.series[0].data=data.num;
ec_left1.setOption(ec_left1_option);
}
})
}
2.1.3图形绘制
- ECharts是一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。
- 我们引入我们所需要option,就可以绘制出我们需要的图表
var ec_left1 = echarts.init(document.getElementById('l1'),"dark");
var ec_left1_option = {
//标题样式
title : {
text : "学历与岗位",
textStyle : {
color : 'white',
},
left : 'left'
},
color: ['#3398DB'],
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value',
show: true
},
series: [{
data: [],
type: 'bar',
barMaxWidth:"50%"
}]
};
ec_left1.setOption(ec_left1_option)
2.2中国地图绘制
- 首先我们需要导入China.js。
- 依旧需要写入相应的依赖。
var ec_center = echarts.init(document.getElementById('c2'), "dark");
var mydata = []
var ec_center_option = {
title: {
text: '岗位分布地图',
subtext: '',
x: 'left'
},
tooltip: {
trigger: 'item'
},
//左侧小导航图标
visualMap: {
show: true,
x: 'left',
y: 'bottom',
textStyle: {
fontSize: 8,
},
splitList: [{ start: 1,end: 9 },
{start: 10, end: 49 },
{ start: 50, end: 99 },
{ start: 100, end: 999 },
],//这设置是数据分等级
color: ['#00ffff','#00eeee', '#98f5ff', '#bbffff',]
},
//配置属性
series: [{
name: '岗位数',
type: 'map',
mapType: 'china',
roam: true, //拖动和缩放
itemStyle: {
normal: {
borderWidth: .5, //区域边框宽度
borderColor: '#009fe8', //区域边框颜色
areaColor: "#f8f8ff", //区域颜色
},
emphasis: { //鼠标滑过地图高亮的相关设置
borderWidth: .5,
borderColor: '#4b0082',
areaColor: "#fff",
}
},
label: {
normal: {
show: true, //省份名称
fontSize: 8,
},
emphasis: {
show: true,
fontSize: 8,
}
},
data:[] //mydata //数据
}]
};
ec_center.setOption(ec_center_option)
注意:
- 在爬取前面爬取数据中有很多的城市地名都是县级或者地级市这样的,因此我们需要将这些城市名统一到省级行政单位。只让我们的数据中只有重庆、湖北这样省级的格式。
- 同时小伙伴需要注意数据爬取的时候并不是会爬取到全国的所有的省级单位都有数据,所以我在数据库建立一张表只存全国的身份名称,然后采用两张表联合查询,将没有爬取到的省份的值返回为0.如果没有值返回的话我们绘制的地图当鼠标滑过就不会有值显示只会有个省份名。
[{'name': '北京', 'value': 9}, {'name': '天津', 'value': 0}, {'name': '黑龙江', 'value': 0}, {'name': '辽宁', 'value': 0}, {'name': '吉林', 'value': 0}, {'name': '内蒙古', 'value': 0}, {'name': '河北', 'value': 0}, {'name': '河南', 'value': 1}, {'name': '山东', 'value': 0}, {'name': '山西', 'value': 0}, {'name': '陕西', 'value': 0}, {'name': '安徽', 'value': 6}, {'name': '江苏', 'value': 4}, {'name': '浙江', 'value': 3}, {'name': '福建', 'value': 1}, {'name': '广东', 'value': 56}, {'name': '广西', 'value': 0}, {'name': '湖南', 'value': 1}, {'name': '湖北', 'value': 20}, {'name': '重庆', 'value': 5}, {'name': '贵州', 'value': 0}, {'name': '云南', 'value': 0}, {'name': '四川', 'value': 16}, {'name': '甘肃', 'value': 0}, {'name': '宁夏', 'value': 0}, {'name': '西藏', 'value': 0}, {'name': '新疆', 'value': 0}, {'name': '海南', 'value': 0}, {'name': '澳门', 'value': 0}, {'name': '香港', 'value': 0}, {'name': '台湾', 'value': 0}, {'name': '上海', 'value': 41}, {'name': '江西', 'value': 0}, {'name': '青海', 'value': 0}]
- 数据获取
这里是统计省份出现的次数,两张表联合查询就可以将没爬取到的省份返回值为0.
sql = "SELECT a.country, COUNT(b.country) AS instnum FROM city a LEFT JOIN job b ON a.country = b.country GROUP BY a.country;"
这里查询到的数据格式一个元组格式,还需进一步将数据转化为json格式同时满足我们所需要的格式。
def get_c2_data():
res = []
for tup in utils.get_c2_data():
# print(tup)
res.append({"name":tup[0],"value":int(tup[1])})
print(res)
return jsonify({"data":res})
后面的处理和前面折线图的数据交互异曲同工,这里就不在赘述。
2.3滚动字幕设计 <div id="risk_wrapper">
<div id="risk_wrapper_li1">
<ul>
</ul>
</div>
<div id="risk_wrapper_li2"></div>
</div>
function start_roll() {
var speed = 20; // 可自行设置文字滚动的速度
var wrapper = document.getElementById('risk_wrapper');
var li1 = document.getElementById('risk_wrapper_li1');
var li2 = document.getElementById('risk_wrapper_li2');
li2.innerHTML = li1.innerHTML //克隆内容
function Marquee() {
if (li2.offsetHeight - wrapper.scrollTop <= 0) //当滚动至demo1与demo2交界时
wrapper.scrollTop -= li1.offsetHeight //demo跳到最顶端
else {
wrapper.scrollTop++ //如果是横向的 将 所有的 height top 改成 width left
}
}
var MyMar = setInterval(Marquee, speed) //设置定时器
wrapper.onmouseover = function () {
clearInterval(MyMar) //鼠标移上时清除定时器达到滚动停止的目的
}
wrapper.onmouseout = function () {
MyMar = setInterval(Marquee, speed) //鼠标移开时重设定时器
}
}
总结
- 需要源码的小伙伴,有积分可以点击下载
- 有问题可以留在评论区,我会尽力帮大家解决。
- 这里提供csv版的链接:https://pan.baidu.com/s/1i7zjB1fEQCu7fkYXHlAR0Q
提取码:s1r6
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)