1. 简介
因为想要找到一个数据分析的工作,能够了解到市面上现有的职位招聘信息也会对找工作有所帮助。
今天就来爬取一下智联招聘上数据分析师的招聘信息,并存入本地的MySQL。
2. 页面分析
2.1 找到数据来源
打开智联招聘首页,选择数据分析师职位,跳转进入数据分析师的详情页面。我们需要抓取的数据都呈现在这里。
既然我们想要的数据都在这个页面上,那么就对页面分析一下,这些数据都是用什么方式传输的。
首先右键查看网页源代码,发现职位信息并不在源代码里面。也就是说,职位信息是通过其他来源加载的,并不是直接写入这个网页的代码里。
右键检查,找到network一览进行查看,发现职位信息其实是来源于接口返回的一个json格式的数据。所以我们要抓取的数据就来源于这个接口:
2.2 分析url构成
由2.1,我们已经找到数据来源的url为
https://fe-api.zhaopin.com/c/i/sou?pageSize=90&workExperience=-1&education=-1&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E5%B8%88&kt=3&_v=0.55861702&x-zp-page-request-id=112663d962234d3e8ca52b6e9d5ab4ea-1545222165002-62086
这个url中包含了很多参数,分别对应在职位搜索时加入的条件:
pageSize:指每一页显示的职位数量,也就是通过url一次取多少数
cityId:指职位的城市划分,489代表全国
workExperience:指职位要求的工作经验
education:职位要求的学历水平
companyType:公司的经营类别
employmentType:职位的性质,如实习/全职/兼职
jobWelfareTag:福利
kw:搜索关键字,如果关键字包含汉字,则进行url转码。如:此处,数据分析师转码为%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E5%B8%88
kt/_v/x-zp-request-id:暂时没有弄明白具体是指什么
上面的url只是搜索结果第一页对应的来源,而我们的搜索结果其实是有很多页进行展示的。
通过同样的方式,可以拿到搜索结果接下来几页对应的url。
以第二页对应的url为例:
https://fe-api.zhaopin.com/c/i/sou?start=90&pageSize=90&workExperience=-1&education=-1&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E5%B8%88&kt=3&_v=0.55861702&x-zp-page-request-id=112663d962234d3e8ca52b6e9d5ab4ea-1545222165002-62086
对比之后,发现:区别之处仅仅在于第二页对应的url加入了一个start参数。
start=90,即为第二页的搜索结果。结合pageSize参数,每一页展示90条职位信息。也就是说,start=90表示第二页,start=180表示第三页......
我们就知道了如何去得到每一页搜索结果对应的数据来源了。
3. 抓取数据
3.1 设计数据获取流程
既然我们已经发现了,每一页的职位信息都来自于访问接口后返回的json格式的数据,我们就可以直接访问接口然后读取json就可以得到想要的职位信息了。
步骤如下:
1) 设定关键字访问接口,获得包含职位信息的json数据
2) 从返回的json中提取需要的数据,存入本地MySQL中
3.2 代码实现
因为这些职位数据是没有经过登陆就可以获取的,而且在请求头中并没有发现什么特别的信息。尝试后发现,智联招聘对传输职位信息的这个接口没有做什么反爬限制,直接使用urllib包的urlopen()方法就可以获得数据。
import pymysql import urllib import json import time import random # 在sql中创建表 def create_table_mysql(): db = pymysql.connect(host='localhost', user='root', password='mysqlkey', db='test_db', port=3306) # 创建游标对象cursor cursor = db.cursor() # 执行SQL,如果表存在就删除 cursor.execute('DROP TABLE IF EXISTS zlzp_sjfx') # 创建表 create_table_sql = """ CREATE TABLE zlzp_sjfx( job_number CHAR(100) COMMENT '记录编号', job_type_big_num CHAR(100) COMMENT '职业大分类编号', job_type_big_name CHAR(100) COMMENT '职业大分类名称', job_type_medium_num CHAR(100) COMMENT '职业细分类编号', job_type_medium_name CHAR(100) COMMENT '职业细分类名称', company_num CHAR(100) COMMENT '公司编号', company_url CHAR(200) COMMENT '公司对应url', company_name CHAR(100) COMMENT '公司名称', company_size_num CHAR(100) COMMENT '公司规模编号', company_size CHAR(100) COMMENT '公司规模', company_type_num CHAR(100) COMMENT '公司类型编号', company_type CHAR(100) COMMENT '公司类型', job_url CHAR(200) COMMENT '职位对应url', working_exp_num CHAR(100) COMMENT '工作经验编号', working_exp CHAR(100) COMMENT '工作经验', edu_level_num CHAR(100) COMMENT '教育水平编号', edu_level CHAR(100) COMMENT '教育水平', job_salary CHAR(100) COMMENT '工资', job_type CHAR(100) COMMENT '工作类型', job_name CHAR(100) COMMENT '工作类型', job_location_lat CHAR(100) COMMENT '经度', job_location_lon CHAR(100) COMMENT '纬度', job_city CHAR(100) COMMENT '工作城市', job_updatetime CHAR(100) COMMENT '更新时间', job_createtime CHAR(100) COMMENT '创建时间', job_endtime CHAR(100) COMMENT '结束时间', job_welfare CHAR(100) COMMENT '工作福利' )""" try: # 创建表 cursor.execute(create_table_sql) # 提交执行 db.commit() print('table zlzp_sjfx create done') except: # 回滚 db.rollback() print('table zlzp_sjfx create not done') return db, cursor db, cursor = create_table_mysql() time_0 = time.time() for i in range(500): url = 'https://fe-api.zhaopin.com/c/i/sou?start={}&pageSize=90&cityId=489&workExperience=-1&education=-1&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E5%B8%88&kt=3&_v=0.89067574&x-zp-page-request-id=866368d6313e41c38a6e600b1c5d8082-1545034860140-256948'.format(i*90) if i==0: url = 'https://fe-api.zhaopin.com/c/i/sou?pageSize=90&cityId=489&workExperience=-1&education=-1&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E5%B8%88&kt=3&_v=0.89067574&x-zp-page-request-id=866368d6313e41c38a6e600b1c5d8082-1545034860140-256948' page = urllib.request.urlopen(url).read() data = json.loads(page) time.sleep(random.uniform(1.2, 2.1)) add_sql = """ INSERT INTO zlzp_sjfx (job_number, job_type_big_num, job_type_big_name,job_type_medium_num,job_type_medium_name,company_num,company_url,company_name, company_size_num, company_size, company_type_num, company_type, job_url, working_exp_num, working_exp, edu_level_num, edu_level, job_salary, job_type, job_name, job_location_lat, job_location_lon, job_city, job_updatetime, job_createtime, job_endtime, job_welfare) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) """ for each_job in data['data']['results']: add_data = ( each_job['number'], # 编号 each_job['jobType']['items'][0]['code'], # 职业大分类编号 each_job['jobType']['items'][0]['name'], # 职业大分类名称 each_job['jobType']['items'][1]['code'], # 职业细分类编号 each_job['jobType']['items'][1]['name'], # 职业细分类名称 each_job['company']['number'], # 公司编号 each_job['company']['url'], # 公司对应url each_job['company']['name'], # 公司名称 each_job['company']['size']['code'], # 公司规模编号 each_job['company']['size']['name'], # 公司规模 each_job['company']['type']['code'], # 公司类型编号 each_job['company']['type']['name'], # 公司类型 each_job['positionURL'], # 职位对应url each_job['workingExp']['code'], # 工作经验编号 each_job['workingExp']['name'], # 工作经验 each_job['eduLevel']['code'], # 教育水平编号 each_job['eduLevel']['name'], # 教育水平 each_job['salary'], # 工资 each_job['emplType'], # 工作类型 each_job['jobName'], # 工作名称 each_job['geo']['lat'], # 经度 each_job['geo']['lon'], # 纬度 each_job['city']['display'], # 工作城市 each_job['updateDate'], each_job['createDate'], each_job['endDate'], '/'.join(each_job['welfare']) # 工作福利 ) cursor.execute(add_sql, add_data) try: db.commit() print('page', i, 'done!用时', time.time()-time_0) except: db.rollback() print('page', i, 'not done') # 关闭游标 cursor.close() # 关闭数据库连接 db.close()