文章目录
技术点
- grequests
- pyecharts
- flask
- re
- bs4
实现的功能
- 热门城市的岗位数量Top10
- 热门行业的用人需求Top10
- 学历占比
- Python开发工程师可视化界面(地图)
- Python开发工程师福利图
描述
对于本文的叙述,我们主要分为三大模块:
- 爬虫
- 数据清洗
- 数据可视化
效果图图下:
grequests讲解
本文主要采用了grequests
来实现并发请求。
- requests是Python发送接口非常好用的一个三方库,由k神编写,简单,方便上手快。但是requests发送请求是串行的,即阻塞
- grequests是K神基于genvent+requests编写的一个并发发送请求的库,使用起来非常简单
安装方法:pip install grequests
1.1 grequests简单使用
首先构造一个请求列表,使用grequests.map()
并行发送,得到一个响应
def get_url():
"""构建一个url列表"""
url_list = [grequests.get(f'https://search.51job.com/list/000000,000000,0000,00,9,99,Python%25E5%25BC%2580%25E5%258F%2591%25E5%25B7%25A5%25E7%25A8%258B%25E5%25B8%2588,2,{page}.html', headers=headers) for page in range(1, 200)]
# response_list: 返回的结果为一个请求列表
response_list = grequests.map(url_list)
return response_list
grequests支持get、post、put、delete等requests支持的HTTP请求方法,使用参数和requests一致,发送请求非常简单。
通过遍历res_list可以得到所有请求的返回结果。
1.2 grequests和request性能对比
我们可以对比下requests串行和grequests并行请求100次github.com的时间,示例如下。
使用requests发送请求
import requests
import time
start = time.time()
res_list = [requests.get('https://github.com') for i in range(100)]
print(time.time()-start)
实际消耗约100s+
使用grequests发送
import grequests
import time
start = time.time()
req_list = [grequests.get('https://github.com') for i in range(100)]
res_list = grequests.map(req_list)
print(time.time()-start)
实际消耗约3.5s
前程无忧爬虫
本文主要抓取的是51job上面,关于Python岗位的数据,网站解析主要使用的是re,相关注释均在代码中注明。
# @time:2020/10/8 18:17
# Author:Small-J
# 该文件为前程无忧异步爬虫
import grequests
import re
import pymysql
# 请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36',
'Referer': 'https://search.51job.com/list/000000,000000,0000,00,9,99,%25E5%25A4%25A7%25E6%2595%25B0%25E6%258D%25AE,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=',
'Host': 'search.51job.com',
}
# 数据库连接
conn = pymysql.connect(host='127.0.0.1', user='root', password='root', db='data')
cursor = conn.cursor()
def get_url():
"""构建一个url列表"""
url_list = [
grequests.get(f'https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,{page}.html', headers=headers)
for page in range(200, 400)]
# response_list: 返回的结果为一个请求列表
response_list = grequests.map(url_list)
return response_list
def parsing_data(response):
"""解析数据"""
"""
字段说明:
{'detail_url': 详情url, 'title': 岗位名称, 'company_url': '公司url',
'company_name': '公司名称', 'salary': '工资', 'date_of_issue': '更新时间',
'company_type': '公司类型', 'welfare': '福利', 'workplace':'工作地点',
'experience': '经验', 'education':'学历', 'recruit_number': '招人数'
'company_number' : '公司人数', 'industry': '行业'
}
"""
html = response.text
data_str = re.findall(r'window.__SEARCH_RESULT__ = (.*?)</script>\r\n<div class="clear"></div>\r\n',
html, re.S)
data_str = ''.join(data_str).replace('\\', '').replace('[', '').replace(']', '')
reg1 = re.compile(
r'"job_href":"(.*?)","job_name":"(.*?)".*?"company_href":"(.*?)","company_name":"(.*?)","providesalary_text":"(.*?)".*?"updatedate":"(.*?)".*?,"companytype_text":"(.*?)".*?"jobwelf":"(.*?)".*?"attribute_text":"(.*?)","companysize_text":"(.*?)","companyind_text":"(.*?)"',
re.S)
content_list = re.findall(reg1, data_str)
item = {}
for content in content_list:
item['detail_url'] = content[0]
item['title'] = content[1]
item['company_url'] = content[2]
item['company_name'] = content[3]
item['salary'] = content[4]
item['date_of_issue'] = content[5]
item['company_type'] = content[6]
item['welfare'] = content[7]
item['workplace'] = content[8].split(',')[0].replace('"', '')
item['experience'] = [content[8].split(',')[1] if len(content[8].split(',')) == 4 else ''][0].replace('"', '')
field = content[8].split(',')
education = []
if len(field) == 4:
education.append(field[2])
elif len(field) == 3:
education.append(field[1])
elif len(field) == 2:
education.append(' ')
item['education'] = education[0].replace('"', '')
item['recruit_number'] = field[-1].replace('"', '')
item['company_number'] = content[9]
item['industry'] = content[10]
yield item
def write_mysql(data_object):
sql = 'insert into search_data(detail_url, title, company_url, company_name, salary, date_of_issue, company_type, welfare, workplace, experience, education, recruit_number, company_number, industry) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'
for data in data_object:
print(data)
args = (data['detail_url'], data['title'], data['company_url'], data['company_name'], data['salary'],
data['date_of_issue'], data['company_type'], data['welfare'], data['workplace'], data['experience'],
data['education'], data['recruit_number'], data['company_number'], data['industry'])
cursor.execute(sql, args)
conn.commit()
def main():
"""主程序入口"""
response_list = get_url()
for response in response_list:
data_object = parsing_data(response)
write_mysql(data_object)
if __name__ == '__main__':
main()
数据清洗
数据清洗,主要清洗的是该数据是否含有空值,如果含有则删除该数据
连接数据库
connect = pymysql.connect('localhost', 'root', 'root', 'data')
cursor = connect.cursor()
cursor.execute('select * from search_data')
content = cursor.fetchall()
使用Pandas重构二维数组
df = pd.DataFrame(data=content, columns=['title', 'company_name', 'salary', 'date_of_issue', 'company_type','welfare','workplace', 'experience', 'education', 'recruit_number', 'company_number','industry', 'detail_url', 'company_url'])
数据去重
df.drop_duplicates(subset=["title", "company_name", "workplace"], inplace=True)
数据可视化
热门城市的岗位数据Top10
从图中可以看出基于Python开发工程师的岗位一般分布在一线城市最为多
热门行业的用人需求Top10
从招聘行业来看计算机软件行业、互联网、电子商务、计算机服务用人需求特别多。
学历分布
从招聘职位来看,本科学历占大部分,其次是专科
Python开发工程师可视化界面
岗位具体分布的地点
Python开发工程师福利图
从开发岗位招聘需求词云图来看,一般福利年终奖,餐饮补贴,员工旅游等。
代码已上传Github : https://github.com/Small-J-gif/Job_Spider