python爬取拉勾网数据并进行数据可视化

爬取拉勾网关于python职位相关的数据信息,并将爬取的数据已csv各式存入文件,而后对csv文件相关字段的数据进行清洗,并对数据可视化展现,包括柱状图展现、直方图展现、词云展现等并根据可视化的数据作进一步的分析,其他分析和展现读者可自行发挥和扩展包括各类分析和不一样的存储方式等。。。。。html

1、爬取和分析相关依赖包

  1. Python版本: Python3.6
  2. requests: 下载网页
  3. math: 向上取整
  4. time: 暂停进程
  5. pandas:数据分析并保存为csv文件
  6. matplotlib:绘图
  7. pyecharts:绘图
  8. statsmodels:统计建模
  9. wordcloud、scipy、jieba:生成中文词云
  10. pylab:设置画图能显示中文

在以上安装或使用过程当中可能读者会遇到安装或导入失败等问题自行百度,选择依赖包的合适版本python

2、分析网页结构

经过Chrome搜索'python工程师',而后右键点击检查或者F12,,使用检查功能查看网页源代码,当咱们点击下一页观察浏览器的搜索栏的url并无改变,这是由于拉勾网作了反爬虫机制, 职位信息并不在源代码里,而是保存在JSON的文件里,所以咱们直接下载JSON,并使用字典方法直接读取数据.便可拿到咱们想要的python职位相关的信息,面试

待爬取的python工程师职位信息以下:正则表达式

为了能爬到咱们想要的数据,咱们要用程序来模拟浏览器来查看网页,因此咱们在爬取的过程当中会加上头信息,头信息也是咱们经过分析网页获取到的,经过网页分析咱们知道该请求的头信息,以及请求的信息和请求的方式是POST请求,这样咱们就能够该url请求拿到咱们想的数据作进一步处理json

爬取网页信息代码以下:api

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%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88?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_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90?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格式的文件中.浏览器

程序运行结果如图: cookie

爬取全部python相关职位信息以下:网络

3、数据清洗后入库

数据清洗其实会占用很大一部分工做,咱们在这里只作一些简单的数据分析后入库。在拉勾网输入python相关的职位会有18988个。你能够根据工做中需求选择要入库的字段,并对一些字段作进一步的筛选,好比咱们能够去除职位名称中为实习生的岗位,过滤指定的字段区域在咱们指定区域的职位,取字段薪资的平均值,以最低值和差值的四分之一为平均值等等根据需求自由发挥session

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)

4、数据可视化展现

下面是对数据的可视化展现,仅以部分视图进行一些可视化的展现,若是读者想对其余字段作一些展现以及想使用不一样的视图类型进行展现,请自行发挥,注:如下代码中引入的模块见最后的完整代码

一、绘制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()

运行结果以下:

二、绘制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()

运行结果以下:

三、绘制基于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')

运行结果以下:

 

四、绘制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()

运行结果以下:

5、爬虫及可视化完整代码

完整代码在下面,代码均测试可正常运行,感兴趣的小伙伴可去尝试和了解其中的使用方法,如运行或者模块安装等失败能够在评论区进行留言,让咱们一同解决吧

若是你以为对你有帮助能够点个赞哦,原创内容转载需说明出处!!!

一、爬虫完整代码

为了防止咱们频繁请求一个网站被限制ip,咱们在爬取每一页后选择睡一段时间,固然你也可使用代理等其余方式自行实现

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%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88?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_%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90?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()

二、可视化完整代码

数据可视化涉及到matplotlib、jieba、wordcloud、pyecharts、pylab、scipy等等模块的使用,读者能够自行了解各个模块的使用方法,和其中涉及的各类参数

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)


"""一、绘制python薪资的频率直方图并保存"""
plt.hist(df['月薪'], bins=8, facecolor='#ff6700', edgecolor='blue')  # bins是默认的条形数目
plt.xlabel('薪资(单位/千元)')
plt.ylabel('频数/频率')
plt.title('python薪资直方图')
plt.savefig('python薪资分布.jpg')
plt.show()

"""二、绘制饼状图并保存"""
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()

"""三、绘制福利待遇的词云"""
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()

"""四、基于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')
相关文章
相关标签/搜索