用Python实现一个面向主题的网络爬虫程序,并完成以下内容:
(注:每人一题,主题内容自选,所有设计内容与源代码需提交到博客园平台)
一、主题式网络爬虫设计方案(15分)
1.主题式网络爬虫名称
爬取智联招聘网站的厦门地区java工程师的招聘
2.主题式网络爬虫爬取的内容与数据特征分析
爬取的内容涉及如下:公司所在的地点、公司名称、公司规模和类型(民营,上市公司,股份制企,国企……),岗位职责,技能要求,薪资,工作经验要求以及学历要求。
数据特征分析:对薪资分布,学历要求,工作经验做成相关数据透视表,即为薪资分布饼状图和柱状图,学历要求饼状图和折线图工作经验柱状图,以及学历_薪资的分布图
3.主题式网络爬虫设计方案概述(包括实现思路与技术难点)
执行爬虫项目过程中有时返回的不是HTML页面而是json数据格式,
(1)打开浏览器,输入智联招聘关键字,登录进去才可以搜索相关职位
(2)f12打开调试者工具,f5刷新。
(3)点击下一页发现浏览器网址栏没有变化,所以判断可能是异步加载,
去调试者工具中点击xhr,在其中发现携带网页内容的json数据。查看该调数据的请求头,
(4)找到真正json数据的网址。点击下一页,发现该网址的结构只有start参数呈现90步长的变动,因为使用for循环即可实现url的构造。
(5)接下来使用requests请求网址,然后从响应的json数据中取出目标数据存入csv文件即可
二、主题页面的结构特征分析(15分)
1.主题页面的结构特征
2.Htmls页面解析
智联招聘上的网页由于是异步加载,直接请求是没有数据的,所以在页面上通过鼠标右击查看页面源代码可以得到HTML页面解析
3.节点(标签)查找方法与遍历方法(必要时画出节点树结构)
网页由于是异步加载,直接请求是没有数据的,所以通过:selenium库把整个网页下载下来,进行节点分析。selenium 是一个用于Web应用程序测试的工具
三、网络爬虫程序设计(60分)
爬虫程序主体要包括以下各部分,要附源代码及较详细注释,并在每部分程序后面提供输出结果的截图。
1.数据爬取与采集(完整代码)
# -*- coding: utf-8 -*- # @Author :LMD # @FILE : 智联招聘.py # @Time : 2019/12/1 15:35 # @Software : PyCharm import requests import time import random import csv import codecs import json def get_json_content(url,headers,writer): #模拟浏览器发送请求: rq = requests.get(url, headers=headers) #设置随机休眠:不然爬取速度过快,会被封IP time.sleep(random.choice(range(1,3))) #取出json数据 rq_json=rq.json()['data']['results'] #取出的json格式数据是列表,循环取出 for i in rq_json: # pprint(i) #工作名称 jobName=i['jobName'] print(jobName) #城市 try: city=i['city']['display'] except: city='暂无' print(city) #工作经验 try: workingExp=i['workingExp']['name'] except: workingExp='暂无' print(workingExp) #学历 try: eduLevel=i['eduLevel']['name'] except: eduLevel='暂无' print(eduLevel) #薪资 salary=i['salary'] print(salary) #工作类型 try: jobType=i['jobType']['items'][0]['name'] except: jobType='暂无' print(jobType) #雇主印象 try: bestEmployerLabel_list=i['bestEmployerLabel'] #定义一个空列表 bestEmployerLabels=[] #取出的bestEmployerLabel_list是列表,循环取出其包裹的字典,并取value值 for x in bestEmployerLabel_list: state_velue=x['value'] #将取出的值依次添加到定义的列表中 bestEmployerLabels.append(state_velue) #列表转字符,并用,分隔 bestEmployerLabel=','.join(bestEmployerLabels) if len(bestEmployerLabel)==0: bestEmployerLabel = '暂无' except: bestEmployerLabel='暂无' print(bestEmployerLabel) #公司名称 company=i['company']['name'] print(company) #公司规模 try: company_size=i['company']['size']['name'] except: company_size='暂无' print(company_size) #公司类型 try: company_type=i['company']['type']['name'] except: company_type='暂无' print(company_type) #任职类型 emplType=i['emplType'] print(emplType) #职位亮点 welfare=','.join(i['welfare']) print(welfare) #招聘现状 timeState=i['timeState'] print(timeState) #更新时间 updateDate=i['updateDate'] print(updateDate) #技能要求标签 try: skillLabel_lists=i['positionLabel'] #取出的是str类型,使用json.loads()转json类型 skillLabel_list=json.loads(skillLabel_lists)['skillLabel'] # 定义一个空列表 skillLabels=[] #循环该列表,并取值 for y in skillLabel_list: sat_v=y['value'] # 将取出的值依次添加到定义的列表中 skillLabels.append(sat_v) # 列表转字符,并用,分隔 skillLabel=','.join(skillLabels) if len(skillLabel)==0: skillLabel = '暂无' except: skillLabel='暂无' print(skillLabel) #详情页链接 positionURL=i['positionURL'] print(positionURL) #将各个字段存入csv writer.writerow([jobName,city,workingExp,eduLevel,salary,jobType,bestEmployerLabel,company,company_size,company_type,emplType,welfare,timeState,updateDate,skillLabel,positionURL]) print('*' * 50) #关闭csv文件 fp.close() if __name__=='__main__': # 第一次打开,为了防止在windows下直接打开csv文件出现乱码,指定编码 with open("data.csv", "ab+") as fp: fp.write(codecs.BOM_UTF8) # 中文需要指定编码,'a+',表示在后面添加新数据,不覆盖。newline=''去除csv文件后面的空行 fq = open('data.csv', 'a+', newline='', encoding='utf-8') #定义writer writer = csv.writer(fq) #第一行写入标题 writer.writerow(['工作名称', '城市', '工作经验', '学历', '薪资', '工作类型', '雇主印象', '公司名称', '公司规模', '公司类型', '任职类型', '职位亮点','招聘现状', '更新时间', '技能要求', '详情链接']) # 总共5页 for offset in range(6): page=90*offset #构建请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36', } #构建url,start= ,这里改变,即可实现翻页start=0是第一页,start=90是第二页,以此类推 url='https://fe-api.zhaopin.com/c/i/sou?start={}&pageSize=90&cityId=682&salary=0,0&workExperience=-1&education=-1&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw=java开发工程师&kt=3'.format(str(page)) #运行get_json_content函数 get_json_content(url,headers,writer)
程序运行结果如下:
爬取出来的数据存为CSV文件(data.csv)
2.对数据进行清洗和处理
import pandas as pd import csv df=pd.read_csv('C:/Users/LMD/CSVFile/data.csv', encoding='utf-8') df.head()#查询数据前五条
df.count()#再查看数据量
df.duplicated()#查看文件中的重复值 df=df.drop_duplicates()#去掉文件中的重复值 df.count()#再查看数据量
3.文本分析(可选):jieba分词、wordcloud可视化
(1)待遇--分词
(2)技能--分词
4.数据分析与可视化
(例如:数据柱形图、直方图、散点图、盒图、分布图、数据回归分析等)
先导要用到的模块
import pandas as pd #数据框操作 import matplotlib.pyplot as plt #绘图 import matplotlib as mpl #配置字体 from pyecharts.charts import Pie,Grid from pyecharts.faker import Faker from pyecharts import options as opts from pyecharts.charts import Bar from pyecharts.charts import Line import numpy as np from pyecharts.globals import ThemeType
4.1学历柱状图
def sava_bar(): mpl.rcParams['font.sans-serif'] = ['SimHei'] #这个是绘图格式,不写这个的话横坐标无法变成我们要的内容 #配置绘图风格 #设置x,y轴提示文字大小 plt.rcParams['axes.labelsize'] = 12. # 设置x,y轴坐标文字大小 plt.rcParams['xtick.labelsize'] = 12. plt.rcParams['ytick.labelsize'] = 12. plt.rcParams['legend.fontsize'] =10. #设置图片800*800大小 plt.rcParams['figure.figsize'] = [8.,8.] #读取数据的CSV文件 df = pd.read_csv('data.csv') #查看第一行 cow1=df.loc[0] print(cow1) #选择运行的函数 save_eduLevel_bar(df) save_workingExp_bar(df) save_compay_type_pie(df) save_salary_pie(df) save_company_size_barh(df) save_zx(df) save_jingy(df) def save_eduLevel_bar(df): #统计各个学历要求的数量 eduLevel=df['学历'].value_counts() # print(eduLevel) # 绘制柱形图↓ df['学历'].value_counts().plot(kind='bar') #设置x,y轴显示的提示文字 plt.ylabel('数量') plt.xlabel('学历') #设置x轴显示文字为平行显示 plt.xticks(rotation=360) #设置头部信息 plt.title('学历要求分布情况') #设置图片显示具体刻度 for x, y in enumerate(eduLevel): plt.text(x, y, '%s' % y, ha='center', va='bottom') plt.savefig('学历要求柱状图.jpg') # 保存 plt.show() #显示图片 plt.close() # 关闭plt缓存 if __name__=='__main__': sava_bar()
4.2经验柱状图
def save_workingExp_bar(df): # 统计各个工作经验的数量 workingExp = df['工作经验'].value_counts() # 绘制柱形图↓ df['工作经验'].value_counts().plot(kind='bar') # 设置x,y轴显示的提示文字 plt.ylabel('数量') plt.xlabel('工作经验') # 设置x轴显示文字为平行显示 plt.xticks(rotation=360) # 设置头部信息 plt.title('工作经验要求分布情况') # 设置图片显示具体刻度 for x, y in enumerate(workingExp): plt.text(x, y, '%s' % y, ha='center', va='bottom') plt.savefig('工作经验要求柱状图.jpg') plt.show() #显示图片 plt.close()
4.3学历与工资的关系
def benk(df): #取学历是本科的数据,赋值给df1 df1=df[(df['学历'] == '本科')] #取所有学历是本科的薪资,并转换成列表 benke=df1['薪资'].tolist() avg_bs=[] for x in benke: #薪资面议的是字符串,无法计算,排除 if x=='薪资面议': pass else: #薪资去掉K,变成3-5,然后用split分隔,变成['3','5'] #然后去最低值和最高值 benke_num1=float(x.replace('K','').split('-')[0])*1000 benke_num2=float(x.replace('K','').split('-')[1])*1000 #相加除以二,求平均 avg_b=(benke_num1+benke_num2)/2 #将平均值依次添加到表内 avg_bs.append(avg_b) #计算列表内元素的总和 sum_avg_bs=sum(avg_bs) #计算列表内元素的个数 len_avg_bs=len(avg_bs) #计算列表元素的平均值,该值就是所有本科学历的平均薪资 avg_benke=sum_avg_bs/len_avg_bs return avg_benke #学历为硕士,大专,不限的逻辑代码块上面一样# def save_zx(df): benke=benk(df)#本科 zhuanke=zhuank(df)#专科 buxin=buxian(df)#学历不限 shuoshi=suoshi(df)#硕士 xueli_list=['本科','大专','不限','硕士'] xinzi_list=[int(benke),int(zhuanke),int(buxin),int(shuoshi)] # 使用pyecharts绘制饼状图 bar = Line(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) bar.add_xaxis(xueli_list) bar.add_yaxis("平均薪资", xinzi_list) # 不显示数据 # bar.add_yaxis("A", v1,label_opts=opts.LabelOpts(is_show=False)) bar.render('学历_薪资分布折线图.html')
学历_薪资折线图
4.4经验和工资的关系
def s_w(df): df1 = df[(df['工作经验'] == '3-5年')] benke = df1['薪资'].tolist() avg_bs = [] for x in benke: if x == '薪资面议': pass else: benke_num1 = float(x.replace('K', '').split('-')[0]) * 1000 benke_num2 = float(x.replace('K', '').split('-')[1]) * 1000 avg_b = (benke_num1 + benke_num2) / 2 avg_bs.append(avg_b) sum_avg_bs = sum(avg_bs) len_avg_bs = len(avg_bs) avg_benke = sum_avg_bs / len_avg_bs return avg_benke def save_jingy(df): san_wu = s_w(df) #3-5工作经验 yi_san = y_s(df) #一年工作经验 buxian = b_x(df) #工作经验不限 wu_shi = w_s(df) #5-10年的工作经验 wu = w(df) #5年工作经验 shi = s(df) #10年工作经验 yi = y(df) #一年工作经验 xueli_list = ['3_5年', '1_3年', '不限', '5_10年', '无经验', '10年以上', '1年以下'] xinzi_list = [int(san_wu), int(yi_san), int(buxian), int(wu_shi), int(wu), int(shi), int(yi)] # 使用pyecharts绘制饼状图 bar = Line(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) bar.add_xaxis(xueli_list) bar.add_yaxis("平均薪资", xinzi_list) # 不显示数据 # bar.add_yaxis("A", v1,label_opts=opts.LabelOpts(is_show=False)) bar.render('经验_薪资分布折线图.html')
经验_薪资折线图
5.数据持久化
#将各个字段存入csv writer.writerow([jobName,city,workingExp,eduLevel,salary,jobType,bestEmployerLabel,company,company_size,company_type, emplType,welfare,timeState,updateDate,skillLabel,positionURL]) print('*' * 50) #关闭csv文件 fp.close()
6.附完整程序代码(完整代码在本题第一小题)
四、结论(10分)
1.经过对主题数据的分析与可视化,可以得到哪些结论?
厦门地区招聘java工程师的需求还是不少的。这个职位对各个学历要求都有涉及,然而最重要的是经验要求,经验年限越高,工资就越高。对比学历的要求,大部分公司要求是本科,对比学历_薪资折线图,本科和大专的薪资相差不是很大。所以最重要的还是工作经验
2.对本次程序设计任务完成的情况做一个简单的小结。
遇到了很多问题,比如第一次爬取智联招聘网站时怎么爬取不到数据,都是乱码的一堆英文符号。然后各种百度询问他人,原来网页是异步加载,模拟ajax请求,找到了它的json格式的数据url,而且使用这种url也省时省力。还有就是导入模块的问题,有时候明明已经导入模块,jupyter运行的时却出现找不到模块的问题。最后用PyCharm(这个导入模块很方便)可以运行。最后克服了很多困难,学到很多使用爬虫库的方法,自己又进步了许多。
来源:https://www.cnblogs.com/lmd-1111/p/12002833.html