我们先通过百度搜索智联招聘,进入智联招聘官网,一看,傻眼了,需要登录才能查看招聘信息

没办法,用账号登录进去,登录后的网页如下:

输入职位名称点击搜索,显示如下网页:

把这个URL:https://sou.zhaopin.com/?jl=765&kw=软件测试&kt=3   拷贝下来,退出登录,再在浏览器地址栏输入复制下来的URL

哈哈,居然不用登录,也可以显示搜索的职位信息。好了,到这一步,目的达成。

接下来,我们来分析下页面,打开浏览器的开发者工具,选择Network,查看XHR,重新刷新一次页面,可以看到有多个异步加载信息

查看每个请求的返回消息,我们可以找到其中有个请求已JSON方式返回了符合要求的总职位数以及职位链接等信息

点击Headers,查看这个请求的URL:

我们把Request URL复制到浏览器中打开,没错就是我们需要的信息:

分析这个URL:https://fe-api.zhaopin.com/c/i/sou?pageSize=60&cityId=765&workExperience=-1&education=-1&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw=软件测试&kt=3

我们可以知道:

1、pageSize:每页开始的值,第一页是0,第二是60,第三页是120,以此类推

2、cityId:是城市编码,直接输入城市名,也是可以的,比如:深圳

3、kw:搜索时输入的关键词,也就是职位名称

其他的字段都可以不变。

分析完了之后,我们可以开始写代码了:

我们先定义一个日志模块,保存爬虫过程中的日志:
log.py

# !usr/bin/env python3
# -*- coding:utf-8 -*-
import logging
import os
from logging.handlers import TimedRotatingFileHandler
'''
遇到不懂的问题?Python学习交流群:821460695满足你的需求,资料都已经上传群文件,可以自行下载!
'''
class Logger:def __init__(self, logger_name='easton'):self.logger = logging.getLogger(logger_name)logging.root.setLevel(logging.NOTSET)self.log_file_name = 'spider_zhilian.log'self.backup_count = 5# 日志输出级别self.console_output_level = 'WARNING'self.file_output_level = 'DEBUG'# 日志输出格式pattern='%(asctime)s - %(levelname)s - %(message)s'self.formatter = logging.Formatter(pattern)# 日志路径if not os.path.exists('log'):os.mkdir('log')self.log_path = os.path.join(os.getcwd(),'log')def get_logger(self):"""在logger中添加日志句柄并返回,如果logger已有句柄,则直接返回"""if not self.logger.handlers:console_handler=logging.StreamHandler()console_handler.setFormatter(self.formatter)console_handler.setLevel(self.console_output_level)self.logger.addHandler(console_handler)# 每天重新创建一个日志文件,最多保留backup_count份file_handler = TimedRotatingFileHandler(filename=os.path.join(self.log_path, self.log_file_name),when='D',interval=1,backupCount=self.backup_count,delay=True,encoding='utf-8')file_handler.setFormatter(self.formatter)file_handler.setLevel(self.file_output_level)self.logger.addHandler(file_handler)return self.logger
logger = Logger().get_logger()

用一个简单的方法来实现增量爬取,把爬取的URL以hashlib加密,加密后返回32个字符,为了节省内存,只取中间的16个字符,这样也可以保证每个不同的URL有不同的加密字符,把爬取的URL加密字符保存到集合中,在爬取完成后,序列化保存到本地磁盘,下次再次爬取时,反序列化保存的URL到内存,对于已经爬取的URL不再爬取,这样就实现了增量爬取。

URL加密:

def hash_url(url):'''对URL进行加密,取加密后中间16位:param url:已爬取的URLL:return:加密的URL'''m = hashlib.md5()m.update(url.encode('utf-8'))return m.hexdigest()[8:-8]

序列化:

def save_progress(data, path):'''序列化保存已爬取的URL文件:param data:要保存的数据:param path:文件路径:return:'''try:with open(path, 'wb+') as f:pickle.dump(data, f)logger.info('save url file success!')except Exception as e:logger.error('save url file failed:',e)save_progress

反序列化:

def load_progress( path):'''反序列化加载已爬取的URL文件:param path::return:'''logger.info("load url file of already spider:%s" % path)try:with open(path, 'rb') as f:tmp = pickle.load(f)return tmpexcept:logger.info("not found url file of already spider!")return set()load_progress

获取符合要求的职位总页数:从JSON消息中获取numFound字段,这个是总条数,再除以60,向上取整,返回的就是总页数

def get_page_nums(cityname,jobname):'''获取符合要求的工作页数:param cityname: 城市名:param jobname: 工作名:return: 总数'''url = r'https://fe-api.zhaopin.com/c/i/sou?pageSize=60&cityId={}&workExperience=-1&education=-1' \r'&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw={}&kt=3'.format(cityname,jobname)logger.info('start get job count...')try:rec = requests.get(url)if rec.status_code==200:j = json.loads(rec.text)count_nums = j.get('data')['numFound']logger.info('get job count nums sucess:%s'%count_nums)page_nums = math.ceil(count_nums/60)logger.info('page nums:%s' % page_nums)return page_numsexcept Exception as e:logger.error('get job count nums faild:%s',e)get_page_nums

获取每页的职位连接:JSON消息中的positionURL就是职位链接,在这里我们顺便获取职位的创建时间,更新时间,截止时间以及职位福利,以字典返回

def get_urls(start,cityname,jobname):'''获取每页工作详情URL以及部分职位信息:param start: 开始的工作条数:param cityname: 城市名:param jobname: 工作名:return: 字典'''url = r'https://fe-api.zhaopin.com/c/i/sou?start={}&pageSize=60&cityId={}&workExperience=-1&education=-1' \r'&companyType=-1&employmentType=-1&jobWelfareTag=-1&kw={}&kt=3'.format(start,cityname,jobname)logger.info('spider start:%s',start)logger.info('get current page all job urls...')url_list=[]try:rec = requests.get(url)if rec.status_code == 200:j = json.loads(rec.text)results = j.get('data').get('results')for job in results:empltype = job.get('emplType')  # 职位类型,全职or校园if empltype=='全职':url_dict = {}url_dict['positionURL'] = job.get('positionURL') # 职位链接url_dict['createDate'] = job.get('createDate') # 招聘信息创建时间url_dict['updateDate'] = job.get('updateDate') # 招聘信息更新时间url_dict['endDate'] = job.get('endDate') # 招聘信息截止时间positionLabel = job.get('positionLabel')if positionLabel:jobLight = (re.search('"jobLight":\[(.*?|[\u4E00-\u9FA5]+)\]',job.get('positionLabel'))) # 职位亮点url_dict['jobLight'] = jobLight.group(1) if jobLight else Noneelse:url_dict['jobLight'] = Noneurl_list.append(url_dict)logger.info('get current page all job urls success:%s' % len(url_list))return url_listexcept Exception as e:logger.error('get current page all job urls faild:%s', e)return Noneget_urls

在浏览器中输入一个职位链接,查看页面信息

在这里我们以lxml来解析页面,解析结果以字典保存到生成器中

def get_job_info(url_list,old_url):'''获取工作详情:param url_list: 列表:return: 字典'''if url_list:for job in url_list:url = job.get('positionURL')h_url = hash_url(url)if not h_url in old_url:logger.info('spider url:%s'%url)try:response = requests.get(url)if response.status_code == 200:s = etree.HTML(response.text)job_stat = s.xpath('//div[@class="main1 cl main1-stat"]')[0]stat_li_first = job_stat.xpath('./div[@class="new-info"]/ul/li[1]')[0]job_name = stat_li_first.xpath('./h1/text()')[0] # 工作名salary = stat_li_first.xpath('./div/strong/text()')[0] # 月薪stat_li_second = job_stat.xpath('./div[@class="new-info"]/ul/li[2]')[0]company_url = stat_li_second.xpath('./div[1]/a/@href')[0] # 公司URLcompany_name = stat_li_second.xpath('./div[1]/a/text()')[0] # 公司名称city_name = stat_li_second.xpath('./div[2]/span[1]/a/text()')[0] # 城市名workingExp = stat_li_second.xpath('./div[2]/span[2]/text()')[0] # 工作经验eduLevel = stat_li_second.xpath('./div[2]/span[3]/text()')[0] # 学历amount = stat_li_second.xpath('./div[2]/span[4]/text()')[0] # 招聘人数job_text = s.xpath('//div[@class="pos-ul"]//text()') # 工作要求job_desc = ''for job_item in job_text:job_desc = job_desc+job_item.replace('\xa0','').strip('\n')job_address_path = s.xpath('//p[@class="add-txt"]/text()') # 上班地址job_address = job_address_path[0] if job_address_path else Nonecompany_text = s.xpath('//div[@class="intro-content"]//text()') # 公司信息company_info=''for item in company_text:company_info = company_info+item.replace('\xa0','').strip('\n')promulgator = s.xpath('//ul[@class="promulgator-ul cl"]/li')compant_industry = promulgator[0].xpath('./strong//text()')[0] #公司所属行业company_type = promulgator[1].xpath('./strong/text()')[0] #公司类型:民营,国企,上市totall_num = promulgator[2].xpath('./strong/text()')[0] #公司总人数company_addr = promulgator[4].xpath('./strong/text()')[0].strip() #公司地址logger.info('get job info success!')old_url.add(h_url)yield {'job_name':job_name, # 工作名称'salary':salary, # 月薪'company_name':company_name, # 公司名称'eduLevel':eduLevel, # 学历'workingExp':workingExp, # 工作经验'amount':amount, # 招聘总人数'jobLight':job.get('jobLight'), # 职位亮点'city_name':city_name, # 城市'job_address':job_address, # 上班地址'createDate':job.get('createDate'), # 创建时间'updateDate':job.get('updateDate'), # 更新时间'endDate':job.get('endDate'), # 截止日期'compant_industry':compant_industry, # 公司所属行业'company_type':company_type, # 公司类型'totall_num':totall_num, # 公司总人数'company_addr':company_addr, # 公司地址'job_desc':job_desc, # 岗位职责'job_url':'url', # 职位链接'company_info':company_info, # 公司信息'company_url':company_url # 公司链接}except Exception as e:logger.error('get job info failed:',url,e)get_job_info

输出到CSV

headers = ['职业名', '月薪', '公司名', '学历', '经验', '招聘人数', '公司亮点','城市', '上班地址','创建时间', '更新时间', '截止时间', '行业', '公司类型', '公司总人数', '公司地址','岗位描述', '职位链接', '信息', '公司网址']
def write_csv_headers(csv_filename):with open(csv_filename,'a',newline='',encoding='utf-8-sig') as f:f_csv = csv.DictWriter(f,headers)f_csv.writeheader()def save_csv(csv_filename,data):with open(csv_filename,'a+',newline='',encoding='utf-8-sig') as f:f_csv = csv.DictWriter(f,data.keys())f_csv.writerow(data)csv

最后就是主函数了:

def main():if not os.path.exists(output_path):os.mkdir(output_path)for jobname in job_names:for cityname in city_names:logger.info('*'*10+'start spider '+'jobname:'+jobname+'city:'+cityname+'*'*10)total_page = get_page_nums(cityname,jobname)old_url = load_progress('old_url.txt')csv_filename=output_path+'/{0}_{1}.csv'.format(jobname,cityname)if not os.path.exists(csv_filename):write_csv_headers(csv_filename)for i in range(int(total_page)):urls = get_urls(i*60, cityname, jobname)data = get_job_info(urls, old_url)for d in data:save_csv(csv_filename,d)save_progress(old_url,'old_url.txt')logger.info('*'*10+'jobname:'+jobname+'city:'+cityname+' spider finished!'+'*'*10)main

打印爬虫耗时总时间:

city_names = ['深圳','广州']
job_names = ['软件测试','数据分析']
output_path = 'output'
if __name__=='__main__':start_time = datetime.datetime.now()logger.info('*'*20+"start running spider!"+'*'*20)main()end_time = datetime.datetime.now()logger.info('*'*20+"spider finished!Running time:%s"%(start_time-end_time) + '*'*20)print("Running time:%s"%(start_time-end_time))

python爬取智联招聘职位信息(单进程)相关推荐

  1. Python爬取智联招聘职位信息

    这是一次失败的尝试 , 事情是这样的-- 1.起因 前段时间刚刚学完爬虫,于是准备找个项目练练手.因为想要了解一下"数据挖掘"的职位招聘现状,所以我打算对'智联招聘'下手了.网上当 ...

  2. Python爬虫爬取智联招聘职位信息

    目的:输入要爬取的职位名称,五个意向城市,爬取智联招聘上的该信息,并打印进表格中 #coding:utf-8 import urllib2 import re import xlwtclass ZLZ ...

  3. 【Python爬虫案例学习20】Python爬虫爬取智联招聘职位信息

    目的:输入要爬取的职位名称,五个意向城市,爬取智联招聘上的该信息,并打印进表格中 ####基本环境配置: Python版本:2.7 开发工具:pycharm 系统:win10 ####相关模块: im ...

  4. 智联招聘python岗位_Python爬虫爬取智联招聘职位信息

    import urllib2 import re import xlwt '''遇到不懂的问题?Python学习交流群:821460695满足你的需求,资料都已经上传群文件,可以自行下载!''' cl ...

  5. java爬取智联招聘职位信息

    第一次写爬虫,案例比较简单,就在智联招聘网站上爬取职位信息. 技术点:IO流,集合,Jsoup使用,以及前端的知识 代码如下: package com.wty.utils;import java.io ...

  6. 爬虫练习二:爬取智联招聘职位信息

    1. 简介 因为想要找到一个数据分析的工作,能够了解到市面上现有的职位招聘信息也会对找工作有所帮助. 今天就来爬取一下智联招聘上数据分析师的招聘信息,并存入本地的MySQL. 2. 页面分析 2.1 ...

  7. pythton爬取智联招聘职位信息

    前言 在智联招聘https://sou.zhaopin.com/时,发现无法直接去解析获得的html文本,它的数据是用js动态加载的,数据内容存储在json文件中,所以不能用以前的方法使用xpath. ...

  8. python爬取智联招聘网_python爬取智联招聘工作岗位信息

    1 # coding:utf-8 2 # auth:xiaomozi 3 #date:2018.4.19 4 #爬取智联招聘职位信息 5 6 7 import urllib 8 from lxml i ...

  9. selenium+PyQuery+chrome headless 爬取智联招聘求职信息

    最近导师让自己摸索摸索Python爬虫,好了就开始一发不可收拾的地步.正巧又碰到有位同学需要一些求职信息对求职信息进行数据分析,本着练练手的目的写了用Python爬取智联招聘网站的信息.这一爬取不得了 ...

最新文章

  1. 【Linux】9_存储管理交换分区管理Swap
  2. python str转dict_在python中将str转换为dict
  3. wgs84坐标格式转换度分秒_一起爬山吗?寻找GIS坐标系统中“隐秘的角落”
  4. 乐高小颗粒履带机器人_玩转乐高大颗粒积木(四十二)——乐高不倒翁、轮式是挖掘机、大颗粒皮筋车...
  5. 如何对系统中设置的修改记录增加log日志
  6. python合并文件_python把多个文件合并到一个新文件
  7. 编写程序判断两个数组是否相等,然后编写一段类似的程序比较两个 vector。
  8. 战胜棋王后,人工智能是否可以颠覆安全?
  9. java课程心得_Java课程感想
  10. 自然语言处理(NLP)语义分析--文本分类、情感分析、意图识别
  11. 校对双层PDF中的隐藏文本
  12. 产品读书《认知盈余》
  13. 畅游陈德文:中国网游的发展与未来趋势
  14. 让电脑死机c语言,秘技:如何悄无声息的让一台电脑死机
  15. python全角数字_python 半角全角的相互转换
  16. 水仙花数(调用函数)
  17. C语言试题123之有 5 个人坐在一起,问第五个人多少岁?他说比第 4 个人大 2 岁。问第 4 个人岁数,他说比第 3 个人大 2 岁。问第三个人,又说比第 2 人大两岁。问第 2 个人,说比第一个
  18. android查询cpu信息
  19. 6.1-6.30推荐文章汇总
  20. 计算机网络和综合布线的关系,综合布线与计算机网络.ppt

热门文章

  1. Springbootg整合validation整合
  2. Quality Tools for Android
  3. 区块链安全和隐私问题有点与众不同
  4. 亚马逊继续“激进” :Q3吐出超10倍净利
  5. 第十六届中国互联网大会:AI落地才能够发挥价值
  6. Android修改主机名和IP地址问题
  7. matlab绘画三维图形(三)
  8. 相机标定板可以自己打印吗_我可以从手机或相机打印多大的照片?
  9. 计算机组成原理课内实验,【计算机基础论文】计算机组成原理课程实验教学改革(共2885字)...
  10. 实用国际(XX)计量单位表