利用python爬取在前程无忧网搜索python关键字出现的最新的招聘数据,保存到本地Excel,进行数据查看和预处理,然后利用matplotlib进行数据分析和可视化。
目标url:https://www.51job.com/
在前程无忧网输入关键字python,搜索有关的岗位数据。翻页查看这些招聘岗位信息,可以发现url翻页的规律。
检查网页源代码,可以找到想要提取的数据。
爬虫代码如下:
import asyncio import aiohttp import logging import datetime import re import pandas as pd logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s') start = datetime.datetime.now() class Spider(object): def __init__(self): self.semaphore = asyncio.Semaphore(6) self.headers = { 'Connection': 'Keep-Alive', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Host': 'search.51job.com', 'Referer': 'https://search.51job.com/list/000000,000000,0000,00,9,99,Python,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24' } async def scrape(self, url): async with self.semaphore: session = aiohttp.ClientSession(headers=self.headers) response = await session.get(url) await asyncio.sleep(1) result = await response.text() await session.close() return result async def scrape_index(self, page): url = f'https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,{page}.html?lang=c&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=' text = await self.scrape(url) await self.parse(text) await asyncio.sleep(1) async def parse(self, text): # 正则匹配提取数据 try: job_name = re.findall('"job_name":"(.*?)",', text) # 职位 company_name = re.findall('"company_name":"(.*?)",', text) # 公司名称 salary = re.findall('"providesalary_text":"(.*?)",', text) salary = [i.replace('\\', '') for i in salary] # 薪酬 去掉 \ 符号 city = re.findall('"workarea_text":"(.*?)",', text) # 城市 job_welfare = re.findall('"jobwelf":"(.*?)",', text) # 职位福利 attribute_text = re.findall('"attribute_text":(.*?),"companysize_text"', text) attribute_text = ['|'.join(eval(i)) for i in attribute_text] companysize = re.findall('"companysize_text":"(.*?)",', text) # 公司规模 category = re.findall('"companyind_text":"(.*?)",', text) category = [i.replace('\\', '') for i in category] # 公司所属行业 去掉 \ 符号 datas = pd.DataFrame({'company_name': company_name, 'job_name': job_name, 'companysize': companysize, 'city': city, 'salary': salary, 'attribute_text': attribute_text, 'category': category, 'job_welfare': job_welfare}) datas.to_csv('job_info.csv', mode='a+', index=False, header=True) logging.info({'company_name': company_name, 'job_name': job_name, 'company_size': companysize, 'city': city, 'salary': salary, 'attribute_text': attribute_text, 'category': category, 'job_welfare': job_welfare}) except Exception as e: print(e) def main(self): # 爬取200页的数据 scrape_index_tasks = [asyncio.ensure_future(self.scrape_index(page)) for page in range(1, 201)] loop = asyncio.get_event_loop() tasks = asyncio.gather(*scrape_index_tasks) loop.run_until_complete(tasks) if __name__ == '__main__': spider = Spider() spider.main() delta = (datetime.datetime.now() - start).total_seconds() print("用时:{:.3f}s".format(delta))
运行效果如下:
爬取了200页的招聘数据,共10000条招聘信息,用时49.919s。
import pandas as pd df = pd.read_csv('job_info.csv') # 异步爬虫爬取数据时 datas.to_csv('job_info.csv', mode='a+', index=False, header=True) 删除多的列名 df1 = df[df['salary'] != 'salary'] # 查看前10行 df1.head(10)
# city那一列数据 处理为城市
# 按 - 分割 expand=True 0那一列重新赋值给df['city']
df1['city'] = df1['city'].str.split('-', expand=True)[0]
df1.head(10)
# 经验要求 学历要求 有的话是在attribute_text列里
df['attribute_text'].str.split('|', expand=True)
df1['experience'] = df1['attribute_text'].str.split('|', expand=True)[1]
df1['education'] = df1['attribute_text'].str.split('|', expand=True)[2]
df1
保存为已清洗数据
df1.to_csv('已清洗数据.csv', index=False)
查看索引、数据类型和内存信息
df2 = pd.read_csv('已清洗数据.csv') df2.info()
代码如下:
import pandas as pd import random import matplotlib.pyplot as plt import matplotlib as mpl df = pd.read_csv('已清洗数据.csv') # 有些是异地招聘 过滤掉 data = df[df['city'] != '异地招聘']['city'].value_counts() city = list(data.index)[:10] # 城市 nums = list(data.values)[:10] # 岗位数 print(city) print(nums) colors = ['#FF0000', '#0000CD', '#00BFFF', '#008000', '#FF1493', '#FFD700', '#FF4500', '#00FA9A', '#191970', '#9932CC'] random.shuffle(colors) # 设置大小 像素 plt.figure(figsize=(9, 6), dpi=100) # 设置中文显示 mpl.rcParams['font.family'] = 'SimHei' # 绘制柱形图 设置柱条的宽度和颜色 # color参数 每根柱条配置不同颜色 plt.bar(city, nums, width=0.5, color=colors) # 添加描述信息 plt.title('招聘岗位数最多的城市Top10', fontsize=16) plt.xlabel('城市', fontsize=12) plt.ylabel('岗位数', fontsize=12) # 展示图片 plt.show()
运行效果如下:
['上海', '深圳', '广州', '北京', '杭州', '成都', '武汉', '南京', '苏州', '长沙'] [2015, 1359, 999, 674, 550, 466, 457, 444, 320, 211]
上海、深圳、广州、北京提供了很多岗位,杭州、成都、武汉、南京等城市的招聘岗位数量也比较可观。
代码如下:
import pandas as pd import matplotlib.pyplot as plt import matplotlib as mpl df = pd.read_csv('已清洗数据.csv')['salary'] part_interval = ["5K-10K", "10K-15K", "15K-20K", "20K-25K", "25K-30K", "30K-35K", "35-50K", "50K以上"] level1, level2, level3, level4, level5, level6, level7, level8 = 0, 0, 0, 0, 0, 0, 0, 0 salary = None for i in df.values: if str(i) == 'nan': pass elif i[-3:] == '万/月': i = i.replace('万/月', '-万/月') x = i.split('-') salary = (float(x[0]) + float(x[1])) * 10 / 2 elif i[-3:] == '千/月': i = i.replace('千/月', '-千/月') x = i.split('-') salary = (float(x[0]) + float(x[1])) / 2 elif i[-3:] == '万/年': i = i.replace('万/年', '-万/年') x = i.split('-') salary = (float(x[0]) + float(x[1])) / 2 * 10 / 12 else: continue if 5 < salary <= 10: level1 += 1 elif 10 < salary <= 15: level2 += 1 elif 15 < salary <= 20: level3 += 1 elif 20 < salary <= 25: level4 += 1 elif 25 < salary <= 30: level5 += 1 elif 30 < salary <= 35: level6 += 1 elif 35 < salary <= 50: level7 += 1 else: level8 += 1 nums = [level1, level2, level3, level4, level5, level6, level7, level8] colors = ['#0000CD', '#FF0000', '#FFD700', '#7FFF00', '#FF1493', '#9400D3', '#FF8C00', '#87CEFA'] # 设置中文显示 mpl.rcParams['font.family'] = 'SimHei' # 设置大小 像素 plt.figure(figsize=(9, 6), dpi=100) plt.axes(aspect='equal') # 保证饼图是个正圆 explodes = [0, 0, 0, 0.1, 0.2, 0.3, 0.4, 0.5] plt.pie(nums, pctdistance=0.75, shadow=True, colors=colors, autopct='%.2f%%', explode=explodes, startangle=15, labeldistance=1.1, ) # 设置图例 调节图例位置 plt.legend(part_interval, bbox_to_anchor=(1.0, 1.0)) plt.title('招聘岗位的薪酬分布', fontsize=15) plt.show()
运行效果如下:
招聘岗位给的薪酬在5K-10K和10K-15K区间所占的比例较大,也有一定比例的50K以上的高薪资岗位。
mport pandas as pd import matplotlib.pyplot as plt import matplotlib as mpl df = pd.read_csv(r'已清洗数据.csv')['education'] data = df.value_counts() labels = ['大专', '本科', '硕士', '博士'] nums = [data[i] for i in labels] print(labels) print(nums) colors = ['cyan', 'red', 'yellow', 'blue'] # 设置中文显示 mpl.rcParams['font.family'] = 'SimHei' # 设置显示风格 plt.style.use('ggplot') # 设置大小 像素 plt.figure(figsize=(8, 6), dpi=100) # 绘制水平柱状图 plt.barh(labels, nums, height=0.36, color=colors) plt.title('招聘岗位对学历的要求', fontsize=16) plt.xlabel('岗位数量', fontsize=12) plt.show()
运行效果如下:
['大专', '本科', '硕士', '博士'] [2052, 6513, 761, 45]
由于得到的工作经验列里的数据并不规范,统计时需做特殊处理
代码如下:
import pandas as pd import matplotlib.pyplot as plt import matplotlib as mpl df = pd.read_csv(r'已清洗数据.csv')['experience'] # 查看统计情况 data = df.value_counts() print(data) labels = ['无需经验', '1年经验', '2年经验', '3-4年经验', '5-7年经验', '8-9年经验', '10年以上经验'] nums = [data[i] for i in labels] # 要求是在校生\应届生、本科 处理为无需经验 硕士处理为1年经验 博士处理为3-4年经验 nums[0] = nums[0] + 661 + 182 nums[1] = nums[1] + 59 nums[3] = nums[3] + 11 print(labels) print(nums) colors = ['#0000CD', '#FF0000', '#FFD700', '#7FFF00', '#FF1493', '#9400D3', '#87CEFA'] # 设置中文显示 mpl.rcParams['font.family'] = 'SimHei' # 设置显示风格 plt.style.use('ggplot') # 设置大小 像素 plt.figure(figsize=(9, 6), dpi=100) # 绘制水平柱状图 plt.barh(labels, nums, height=0.5, color=colors) plt.title('招聘岗位对工作经验的要求', fontsize=16) plt.xlabel('岗位数量', fontsize=12) plt.show()
运行效果如下:
3-4年经验 3361 2年经验 2114 1年经验 1471 5-7年经验 1338 在校生\/应届生 661 无需经验 417 本科 182 8-9年经验 105 10年以上经验 64 硕士 59 招1人 57 招若干人 57 招2人 42 大专 30 招3人 14 博士 11 招5人 9 招4人 5 招10人 2 招7人 1 Name: experience, dtype: int64 ['无需经验', '1年经验', '2年经验', '3-4年经验', '5-7年经验', '8-9年经验', '10年以上经验'] [1260, 1530, 2114, 3372, 1338, 105, 64]
代码如下:
import pandas as pd import collections from wordcloud import WordCloud import matplotlib.pyplot as plt df = pd.read_csv(r'已清洗数据.csv')['category'] data = list(df.values) word_list = [] for i in data: x = i.split('/') for j in x: word_list.append(j) word_counts = collections.Counter(word_list) # 绘制词云 my_cloud = WordCloud( background_color='white', # 设置背景颜色 默认是black width=900, height=500, font_path='simhei.ttf', # 设置字体 显示中文 max_font_size=120, # 设置字体最大值 min_font_size=15, # 设置子图最小值 random_state=60 # 设置随机生成状态,即多少种配色方案 ).generate_from_frequencies(word_counts) # 显示生成的词云图片 plt.imshow(my_cloud, interpolation='bilinear') # 显示设置词云图中无坐标轴 plt.axis('off') plt.show()
运行效果如下:
代码如下:
import pandas as pd import collections from wordcloud import WordCloud import matplotlib.pyplot as plt df = pd.read_csv(r'已清洗数据.csv')['job_welfare'] # 去除NaN 就地修改 df.dropna(axis=0, inplace=True) data = list(df.values) word_list = [] for i in data: x = i.split(' ') for j in x: word_list.append(j) word_counts = collections.Counter(word_list) print(word_counts) print(len(word_counts)) # 绘制词云 my_cloud = WordCloud( background_color='white', # 设置背景颜色 默认是black width=800, height=550, font_path='simhei.ttf', # 设置字体 显示中文 max_font_size=112, # 设置字体最大值 min_font_size=12, # 设置子图最小值 random_state=60 # 设置随机生成状态,即多少种配色方案 ).generate_from_frequencies(word_counts) # 显示生成的词云图片 plt.imshow(my_cloud, interpolation='bilinear') # 显示设置词云图中无坐标轴 plt.axis('off') plt.show()
运行效果如下:
职位福利关键词中出现频率较高的有五险一金、年终奖金、绩效奖金、定期体检、餐饮补贴等
源码获取加群:1136192749
原作者:
叶庭云