爬取拉勾网关于 python 职位相关的数据信息, 并将爬取的数据已 CSV 各式存入文件, 然后对 CSV 文件相关字段的数据进行清洗, 并对数据可视化展示, 包括柱状图展示, 直方图展示, 词云展示等并根据可视化的数据做进一步的分析, 其余分析和展示读者可自行发挥和扩展包括各种分析和不同的存储方式等.....
一, 爬取和分析相关依赖包
Python 版本: Python3.6
requests: 下载网页
math: 向上取整
time: 暂停进程
pandas: 数据分析并保存为 CSV 文件
matplotlib: 绘图
pyecharts: 绘图
statsmodels: 统计建模
wordcloud,scipy,jieba: 生成中文词云
pylab: 设置画图能显示中文
在以上安装或使用过程中可能读者会遇到安装或导入失败等问题自行百度, 选择依赖包的合适版本
二, 分析网页结构
通过 Chrome 搜索'python 工程师', 然后右键点击检查或者 F12,, 使用检查功能查看网页源代码, 当我们点击下一页观察浏览器的搜索栏的 url 并没有改变, 这是因为拉勾网做了反爬虫机制, 职位信息并不在源代码里, 而是保存在 JSON 的文件里, 因此我们直接下载 JSON, 并使用字典方法直接读取数据. 即可拿到我们想要的 python 职位相关的信息,
待爬取的 python 工程师职位信息如下:
为了能爬到我们想要的数据, 我们要用程序来模拟浏览器来查看网页, 所以我们在爬取的过程中会加上头信息, 头信息也是我们通过分析网页获取到的, 通过网页分析我们知道该请求的头信息, 以及请求的信息和请求的方式是 POST 请求, 这样我们就可以该 url 请求拿到我们想的数据做进一步处理
爬取网页信息代码如下:
- import requests
- url = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'
- def get_json(url, num):
- """
- 从指定的 url 中通过 requests 请求携带请求头和请求体获取网页中的信息,
- :return:
- """ url1 ='https://www.lagou.com/jobs/list_python开发工程师?labelWords=&fromSearch=true&suginput='
- headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
- 'Host': 'www.lagou.com',
- 'Referer': 'https://www.lagou.com/jobs/list_数据分析?labelWords=&fromSearch=true&suginput=',
- 'X-Anit-Forge-Code': '0',
- 'X-Anit-Forge-Token': 'None',
- 'X-Requested-With': 'XMLHttpRequest'
- }
- data = {
- 'first': 'true',
- 'pn': num,
- 'kd': 'python 工程师'}
- s = requests.Session()
- print('建立 session:', s, '\n\n')
- s.get(url=url1, headers=headers, timeout=3)
- cookie = s.cookies
- print('获取 cookie:', cookie, '\n\n')
- res = requests.post(url, headers=headers, data=data, cookies=cookie, timeout=3)
- res.raise_for_status()
- res.encoding = 'utf-8'
- page_data = res.JSON()
- print('请求响应结果:', page_data, '\n\n')
- return page_data
- print(get_json(url, 1))
通过搜索我们知道每页显示 15 个职位, 最多显示 30 页, 通过分析网页源代码知道, 可以通过 JSON 里读取总职位数, 通过总的职位数和每页能显示的职位数. 我们可以计算出总共有多少页, 然后使用循环按页爬取, 最后将职位信息汇总, 写入到 CSV 格式的文件中.
程序运行结果如图:
爬取所有 python 相关职位信息如下:
三, 数据清洗后入库
数据清洗其实会占用很大一部分工作, 我们在这里只做一些简单的数据分析后入库. 在拉勾网输入 python 相关的职位会有 18988 个. 你可以根据工作中需求选择要入库的字段, 并对一些字段做进一步的筛选, 比如我们可以去除职位名称中为实习生的岗位, 过滤指定的字段区域在我们指定区域的职位, 取字段薪资的平均值, 以最低值和差值的四分之一为平均值等等根据需求自由发挥
- import pandas as pd
- import matplotlib.pyplot as plt
- import statsmodels.API as sm
- from wordcloud import WordCloud
- from scipy.misc import imread
- from imageio import imread
- import jieba
- from pylab import mpl
- # 使用 matplotlib 能够显示中文
- mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
- mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
- # 读取数据
- df = pd.read_csv('Python_development_engineer.csv', encoding='utf-8')
- # 进行数据清洗, 过滤掉实习岗位
- # df.drop(df[df['职位名称'].str.contains('实习')].index, inplace=True)
- # print(df.describe())
- # 由于 CSV 文件中的字符是字符串形式, 先用正则表达式将字符串转化为列表, 在去区间的均值
- pattern = '\d+'
- # print(df['工作经验'], '\n\n\n')
- # print(df['工作经验'].str.findall(pattern))
- df['工作年限'] = df['工作经验'].str.findall(pattern)
- print(type(df['工作年限']), '\n\n\n')
- avg_work_year = []
- count = 0
- for i in df['工作年限']:
- # print('每个职位对应的工作年限',i)
- # 如果工作经验为'不限'或'应届毕业生', 那么匹配值为空, 工作年限为 0
- if len(i) == 0:
- avg_work_year.append(0)
- # print('nihao')
- count += 1
- # 如果匹配值为一个数值, 那么返回该数值
- elif len(i) == 1:
- # print('hello world')
- avg_work_year.append(int(''.join(i)))
- count += 1
- # 如果匹配为一个区间则取平均值
- else:
- num_list = [int(j) for j in i]
- avg_year = sum(num_list) / 2
- avg_work_year.append(avg_year)
- count += 1
- print(count)
- df['avg_work_year'] = avg_work_year
- # 将字符串转化为列表, 薪资取最低值加上区间值得 25%, 比较贴近现实
- df['salary'] = df['薪资'].str.findall(pattern)
- #
- avg_salary_list = []
- for k in df['salary']:
- int_list = [int(n) for n in k]
- avg_salary = int_list[0] + (int_list[1] - int_list[0]) / 4
- avg_salary_list.append(avg_salary)
- df['月薪'] = avg_salary_list
- # df.to_csv('python.csv', index=False)
四, 数据可视化展示
下面是对数据的可视化展示, 仅以部分视图进行一些可视化的展示, 如果读者想对其他字段做一些展示以及想使用不同的视图类型进行展示, 请自行发挥, 注: 以下代码中引入的模块见最后的完整代码
1, 绘制 python 薪资的频率直方图并保存
如果我们想看看关于互联网行业 python 工程师相关的岗位大家普遍薪资的一个分部区间在哪个范围, 占据了多达的比例我们就可以借助 matplotlib 库, 来将我们保存在 CSV 文件中的数据进行可视化的展示, 然我们能够更直观的看到数据的一个分部趋势
- # 绘制 python 薪资的频率直方图并保存
- plt.hist(df['月薪'],bins=8,facecolor='#ff6700',edgecolor='blue') # bins 是默认的条形数目
- plt.xlabel('薪资(单位 / 千元)')
- plt.ylabel('频数 / 频率')
- plt.title('python 薪资直方图')
- plt.savefig('python 薪资分布. jpg')
- plt.show()
运行结果如下:
2, 绘制 python 相关职位的地理位置饼状图
通过地理 python 职位地理位置的分部我们可以大致了解 IT 行业主要集中分部在哪些城市, 这样也更利于我们选择地域进行选择性就业, 可以获得更多的面试机会等, 参数可自行调试, 或根据需要添加.
- # 绘制饼状图并保存
- city = df['城市'].value_counts()
- print(type(city))
- # print(len(city))
- label = city.keys()
- print(label)
- city_list = []
- count = 0
- n = 1
- distance = []
- for i in city:
- city_list.append(i)
- print('列表长度', len(city_list))
- count += 1
- if count> 5:
- n += 0.1
- distance.append(n)
- else:
- distance.append(0)
- plt.pie(city_list, labels=label, labeldistance=1.2, autopct='%2.1f%%', pctdistance=0.6, shadow=True, explode=distance)
- plt.axis('equal') # 使饼图为正圆形
- plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
- plt.savefig('python 地理位置分布图. jpg')
- plt.show()
运行结果如下:
3, 绘制基于 pyechart 的城市分布柱状图
pycharts 是 python 中调用百度基于 JS 开发的 echarts 接口, 也可以对数据进行各种可视化操作, 更多数据可视化图形展示, 可参考 echarts 官网: https://www.echartsjs.com/ ,echarts 官网提供了各种实例供我们参考, 如折线图, 柱状图, 饼图, 路径图, 树图等等, 基于 pyecharts 的文档可参考以下官网: https://pyecharts.org/#/ , 更多用法也可自行百度网络资源
- city = df['城市'].value_counts()
- print(type(city))
- print(city)
- # print(len(city))
- keys = city.index # 等价于 keys = city.keys()
- values = city.values
- from pyecharts import Bar
- bar = Bar("python 职位的城市分布图")
- bar.add("城市", keys, values)
- bar.print_echarts_options() # 该行只为了打印配置项, 方便调试时使用
- bar.render(path='a.html')
运行结果如下:
4, 绘制 python 福利相关的词云
词云图又叫文字云, 是对文本数据中出现频率较高的关键词予以视觉上的突出, 形成 "关键词的渲染" 就类似云一样的彩色图片, 从而过滤掉大量的文本信息,, 使人一眼就可以领略文本数据的主要表达意思. 利用 jieba 分词和词云生成 WorldCloud(可自定义背景), 下面就是对 python 相关职位的福利做了一个词云的展示, 可以更直观的看到大多数公司的福利待遇集中在哪些地方
- # 绘制福利待遇的词云
- text = ''for line in df['公司福利']:
- if len(eval(line)) == 0:
- continue
- else:
- for Word in eval(line):
- # print(Word)
- text += Word
- cut_word = ','.join(jieba.cut(text))
- word_background = imread('公主. jpg')
- cloud = WordCloud(
- font_path=r'C:\Windows\Fonts\simfang.ttf',
- background_color='black',
- mask=word_background,
- max_words=500,
- max_font_size=100,
- width=400,
- height=800
- )
- word_cloud = cloud.generate(cut_word)
- word_cloud.to_file('福利待遇词云. png')
- plt.imshow(word_cloud)
- plt.axis('off')
- plt.show()
运行结果如下:
五, 爬虫及可视化完整代码
完整代码在下面, 代码均测试可正常运行, 感兴趣的小伙伴可去尝试和了解其中的使用方法, 如运行或者模块安装等失败可以在评论区进行留言, 让我们一同解决吧
- import requests
- import math
- import time
- import pandas as pd
- def get_json(url, num):
- """
- 从指定的 url 中通过 requests 请求携带请求头和请求体获取网页中的信息,
- :return:
- """ url1 ='https://www.lagou.com/jobs/list_python开发工程师?labelWords=&fromSearch=true&suginput='
- headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
- 'Host': 'www.lagou.com',
- 'Referer': 'https://www.lagou.com/jobs/list_数据分析?labelWords=&fromSearch=true&suginput=',
- 'X-Anit-Forge-Code': '0',
- 'X-Anit-Forge-Token': 'None',
- 'X-Requested-With': 'XMLHttpRequest'
- }
- data = {
- 'first': 'true',
- 'pn': num,
- 'kd': 'python 工程师'}
- s = requests.Session()
- print('建立 session:', s, '\n\n')
- s.get(url=url1, headers=headers, timeout=3)
- cookie = s.cookies
- print('获取 cookie:', cookie, '\n\n')
- res = requests.post(url, headers=headers, data=data, cookies=cookie, timeout=3)
- res.raise_for_status()
- res.encoding = 'utf-8'
- page_data = res.JSON()
- print('请求响应结果:', page_data, '\n\n')
- return page_data
- def get_page_num(count):
- """
- 计算要抓取的页数, 通过在拉勾网输入关键字信息, 可以发现最多显示 30 页信息, 每页最多显示 15 个职位信息
- :return:
- """
- page_num = math.ceil(count / 15)
- if page_num> 30:
- return 30
- else:
- return page_num
- def get_page_info(jobs_list):
- """
- 获取职位
- :param jobs_list:
- :return:
- """
- page_info_list = []
- for i in jobs_list: # 循环每一页所有职位信息
- job_info = []
- job_info.append(i['companyFullName'])
- job_info.append(i['companyShortName'])
- job_info.append(i['companySize'])
- job_info.append(i['financeStage'])
- job_info.append(i['district'])
- job_info.append(i['positionName'])
- job_info.append(i['workYear'])
- job_info.append(i['education'])
- job_info.append(i['salary'])
- job_info.append(i['positionAdvantage'])
- job_info.append(i['industryField'])
- job_info.append(i['firstType'])
- job_info.append(i['companyLabelList'])
- job_info.append(i['secondType'])
- job_info.append(i['city'])
- page_info_list.append(job_info)
- return page_info_list
- def main():
- url = 'https://www.lagou.com/jobs/positionAjax.json?needAddtionalResult=false'
- first_page = get_json(url, 1)
- total_page_count = first_page['content']['positionResult']['totalCount']
- num = get_page_num(total_page_count)
- total_info = []
- time.sleep(10)
- print("python 开发相关职位总数:{}, 总页数为:{}".format(total_page_count, num))
- for num in range(1, num + 1):
- # 获取每一页的职位相关的信息
- page_data = get_json(url, num) # 获取响应 JSON
- jobs_list = page_data['content']['positionResult']['result'] # 获取每页的所有 python 相关的职位信息
- page_info = get_page_info(jobs_list)
- print("每一页 python 相关的职位信息:%s" % page_info, '\n\n')
- total_info += page_info
- print('已经爬取到第 {} 页, 职位总数为{}'.format(num, len(total_info)))
- time.sleep(20)
- # 将总数据转化为 data frame 再输出, 然后在写入到 CSV 各式的文件中
- df = pd.DataFrame(data=total_info,
- columns=['公司全名', '公司简称', '公司规模', '融资阶段', '区域', '职位名称', '工作经验', '学历要求', '薪资', '职位福利', '经营范围',
- '职位类型', '公司福利', '第二职位类型', '城市'])
- # df.to_csv('Python_development_engineer.csv', index=False)
- print('python 相关职位信息已保存')
- if __name__ == '__main__':
- main()
- import pandas as pd
- import matplotlib.pyplot as plt
- import statsmodels.API as sm
- from wordcloud import WordCloud
- from scipy.misc import imread
- # from imageio import imread
- import jieba
- from pylab import mpl
- # 使用 matplotlib 能够显示中文
- mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
- mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
- # 读取数据
- df = pd.read_csv('Python_development_engineer.csv', encoding='utf-8')
- # 进行数据清洗, 过滤掉实习岗位
- # df.drop(df[df['职位名称'].str.contains('实习')].index, inplace=True)
- # print(df.describe())
- # 由于 CSV 文件中的字符是字符串形式, 先用正则表达式将字符串转化为列表, 在去区间的均值
- pattern = '\d+'
- # print(df['工作经验'], '\n\n\n')
- # print(df['工作经验'].str.findall(pattern))
- df['工作年限'] = df['工作经验'].str.findall(pattern)
- print(type(df['工作年限']), '\n\n\n')
- avg_work_year = []
- count = 0
- for i in df['工作年限']:
- # print('每个职位对应的工作年限',i)
- # 如果工作经验为'不限'或'应届毕业生', 那么匹配值为空, 工作年限为 0
- if len(i) == 0:
- avg_work_year.append(0)
- # print('nihao')
- count += 1
- # 如果匹配值为一个数值, 那么返回该数值
- elif len(i) == 1:
- # print('hello world')
- avg_work_year.append(int(''.join(i)))
- count += 1
- # 如果匹配为一个区间则取平均值
- else:
- num_list = [int(j) for j in i]
- avg_year = sum(num_list) / 2
- avg_work_year.append(avg_year)
- count += 1
- print(count)
- df['avg_work_year'] = avg_work_year
- # 将字符串转化为列表, 薪资取最低值加上区间值得 25%, 比较贴近现实
- df['salary'] = df['薪资'].str.findall(pattern)
- #
- avg_salary_list = []
- for k in df['salary']:
- int_list = [int(n) for n in k]
- avg_salary = int_list[0] + (int_list[1] - int_list[0]) / 4
- avg_salary_list.append(avg_salary)
- df['月薪'] = avg_salary_list
- # df.to_csv('python.csv', index=False)
- """1, 绘制 python 薪资的频率直方图并保存"""
- plt.hist(df['月薪'], bins=8, facecolor='#ff6700', edgecolor='blue') # bins 是默认的条形数目
- plt.xlabel('薪资(单位 / 千元)')
- plt.ylabel('频数 / 频率')
- plt.title('python 薪资直方图')
- plt.savefig('python 薪资分布. jpg')
- plt.show()
- """2, 绘制饼状图并保存"""
- city = df['城市'].value_counts()
- print(type(city))
- # print(len(city))
- label = city.keys()
- print(label)
- city_list = []
- count = 0
- n = 1
- distance = []
- for i in city:
- city_list.append(i)
- print('列表长度', len(city_list))
- count += 1
- if count> 5:
- n += 0.1
- distance.append(n)
- else:
- distance.append(0)
- plt.pie(city_list, labels=label, labeldistance=1.2, autopct='%2.1f%%', pctdistance=0.6, shadow=True, explode=distance)
- plt.axis('equal') # 使饼图为正圆形
- plt.legend(loc='upper left', bbox_to_anchor=(-0.1, 1))
- plt.savefig('python 地理位置分布图. jpg')
- plt.show()
- """3, 绘制福利待遇的词云"""
- text = ''for line in df['公司福利']:
- if len(eval(line)) == 0:
- continue
- else:
- for Word in eval(line):
- # print(Word)
- text += Word
- cut_word = ','.join(jieba.cut(text))
- word_background = imread('公主. jpg')
- cloud = WordCloud(
- font_path=r'C:\Windows\Fonts\simfang.ttf',
- background_color='black',
- mask=word_background,
- max_words=500,
- max_font_size=100,
- width=400,
- height=800
- )
- word_cloud = cloud.generate(cut_word)
- word_cloud.to_file('福利待遇词云. png')
- plt.imshow(word_cloud)
- plt.axis('off')
- plt.show()
- """4, 基于 pyechart 的柱状图"""
- city = df['城市'].value_counts()
- print(type(city))
- print(city)
- # print(len(city))
- keys = city.index # 等价于 keys = city.keys()
- values = city.values
- from pyecharts import Bar
- bar = Bar("python 职位的城市分布图")
- bar.add("城市", keys, values)
- bar.print_echarts_options() # 该行只为了打印配置项, 方便调试时使用
- bar.render(path='a.html')
来源: https://www.cnblogs.com/sui776265233/p/11146969.html