原文地址:http://www.jtahstu.com/blog/s...php
感谢BOSS直聘相对权威的招聘信息,使本人有了此次比较有意思的研究之旅。html
因为爬虫持续爬取 www.zhipin.com 网站,以至产生的服务器压力,本人深感歉意,并无 DDoS 和危害贵网站的意思。python
[2017-12-14更新]
在跑了一晚上以后,服务器 IP 仍是被封了,搞得本人如今家里、公司、云服务器三线做战啊
[2017-12-19更新]
后续把拉勾网的数据也爬到,加了进来
这里须要知道页面的 id 才能生成详细的连接,在 Python爬虫框架Scrapy实战 - 抓取BOSS直聘招聘信息 中,咱们已经拿到招聘信息的大部分信息,里面有个 pid
字段就是用来惟一区分某条招聘,并用来拼凑详细连接的。git
是吧,明眼人一眼就看出来了。web
详情页以下图所示数据库
在详情页中,比较重要的就是职位描述
和工做地址
这两个编程
因为在页面代码中岗位职责
和任职要求
是在一个 div 中的,因此在抓的时候就不太好分,后续须要把这个连体婴儿,分开分析。服务器
使用的库有微信
对应的安装文档依次以下,就不细说了cookie
""" @author: jtahstu @contact: root@jtahstu.com @site: http://www.jtahstu.com @time: 2017/12/10 00:25 """ # -*- coding: utf-8 -*- import requests from bs4 import BeautifulSoup import time from pymongo import MongoClient headers = { 'x-devtools-emulate-network-conditions-client-id': "5f2fc4da-c727-43c0-aad4-37fce8e3ff39", 'upgrade-insecure-requests': "1", 'user-agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36", 'accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", 'dnt': "1", 'accept-encoding': "gzip, deflate", 'accept-language': "zh-CN,zh;q=0.8,en;q=0.6", 'cookie': "__c=1501326829; lastCity=101020100; __g=-; __l=r=https%3A%2F%2Fwww.google.com.hk%2F&l=%2F; __a=38940428.1501326829..1501326829.20.1.20.20; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1501326839; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1502948718; __c=1501326829; lastCity=101020100; __g=-; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1501326839; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1502954829; __l=r=https%3A%2F%2Fwww.google.com.hk%2F&l=%2F; __a=38940428.1501326829..1501326829.21.1.21.21", 'cache-control': "no-cache", 'postman-token': "76554687-c4df-0c17-7cc0-5bf3845c9831" } conn = MongoClient('127.0.0.1', 27017) db = conn.iApp # 链接mydb数据库,没有则自动建立 def init(): items = db.jobs_php.find().sort('pid') for item in items: if 'detail' in item.keys(): # 在爬虫挂掉再此爬取时,跳过已爬取的行 continue detail_url = "https://www.zhipin.com/job_detail/%s.html?ka=search_list_1" % item['pid'] print(detail_url) html = requests.get(detail_url, headers=headers) if html.status_code != 200: # 爬的太快网站返回403,这时等待解封吧 print('status_code is %d' % html.status_code) break soup = BeautifulSoup(html.text, "html.parser") job = soup.select(".job-sec .text") if len(job) < 1: continue item['detail'] = job[0].text.strip() # 职位描述 location = soup.select(".job-sec .job-location") item['location'] = location[0].text.strip() # 工做地点 item['updated_at'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) # 实时爬取时间 res = save(item) # 保存数据 print(res) time.sleep(40) # 停停停 # 保存数据到 MongoDB 中 def save(item): return db.jobs_php.update_one({"_id": item['_id']}, {"$set": item}) if __name__ == "__main__": init()
代码 easy,初学者都能看懂。
在 上一篇文章 中只是爬了 上海-PHP
近300条数据,后续改了代码,把12个城市的 PHP 相关岗位的数据都抓下来了,有3500+条数据,慢慢爬吧,急不来。
像这样
"time" : "发布于03月31日", "time" : "发布于昨天", "time" : "发布于11:31",
这里拿到的都是这种格式的,因此简单处理下
import datetime from pymongo import MongoClient db = MongoClient('127.0.0.1', 27017).iApp def update(data): return db.jobs_php.update_one({"_id": data['_id']}, {"$set": data}) # 把时间校订过来 def clear_time(): items = db.jobs_php.find({}) for item in items: if not item['time'].find('布于'): continue item['time'] = item['time'].replace("发布于", "2017-") item['time'] = item['time'].replace("月", "-") item['time'] = item['time'].replace("日", "") if item['time'].find("昨天") > 0: item['time'] = str(datetime.date.today() - datetime.timedelta(days=1)) elif item['time'].find(":") > 0: item['time'] = str(datetime.date.today()) update(item) print('ok')
"salary" : "5K-12K", #处理成下面的格式 "salary" : { "low" : 5000, "high" : 12000, "avg" : 8500.0 },
# 薪水处理成数字,符合 xk-yk 的数据处理,不符合的跳过 def clear_salary(): items = db.jobs_lagou_php.find({}) for item in items: if type(item['salary']) == type({}): continue salary_list = item['salary'].lower().replace("k", "000").split("-") if len(salary_list) != 2: print(salary_list) continue try: salary_list = [int(x) for x in salary_list] except: print(salary_list) continue item['salary'] = { 'low': salary_list[0], 'high': salary_list[1], 'avg': (salary_list[0] + salary_list[1]) / 2 } update(item) print('ok')
[2017-12-19更新]
这里在处理 Boss 直聘的数据时,比较简单正常,可是后续抓到拉勾网的数据,拉勾网的数据有些不太规范。好比有‘20k以上’这种描述
# 校订拉勾网工做年限描述,以 Boss直聘描述为准 def update_lagou_workyear(): items = db.jobs_lagou_php.find({}) for item in items: if item['workYear'] == '应届毕业生': item['workYear'] = '应届生' elif item['workYear'] == '1年如下': item['workYear'] = '1年之内' elif item['workYear'] == '不限': item['workYear'] = '经验不限' update_lagou(item) print('ok') # 设置招聘的水平,分两次执行 def set_level(): items = db.jobs_zhipin_php.find({}) # items = db.jobs_lagou_php.find({}) for item in items: if item['workYear'] == '应届生': item['level'] = 1 elif item['workYear'] == '1年之内': item['level'] = 2 elif item['workYear'] == '1-3年': item['level'] = 3 elif item['workYear'] == '3-5年': item['level'] = 4 elif item['workYear'] == '5-10年': item['level'] = 5 elif item['workYear'] == '10年以上': item['level'] = 6 elif item['workYear'] == '经验不限': item['level'] = 10 update(item) print('ok')
这里有点坑的就是,通常要求经验不限
的岗位,需求基本都写在任职要求
里了,因此为了统计的准确性,这个等级的数据,后面会被舍弃掉。
[2017-12-14更新]
从后续的平均数据来看,这里的经验不限
,通常要求的是1-3年
左右,可是仍是建议舍弃掉。
[2017-12-19更新]
拉勾网的职位描述和 Boss直聘稍有不一样,须要先校订,而后再设置等级
对于做者这个初学者来讲,这里尚未什么好的方法,知道的同窗,请务必联系做者,联系方式在我的博客里
so , i'm sorry.
为何这两个很差划分出来呢?
由于这里填的并不统一,能够说各类花样,有的要求在前,职责在后,有的又换个名字区分。目前看到的关于要求的有['任职条件', '技术要求', '任职要求', '任职资格', '岗位要求']
这么多说法。而后顺序还不同,有的要求在前,职责在后,有的又反之。
举个栗子
会基本的php编程!可以修改简单的软件!对云服务器和数据库可以运用!懂得微信公众帐号对接和开放平台对接!咱们不是软件公司,是运营公司!想找好的公司学习的陕西基本没有,要到沿海城市去!可是咱们是实用型公司,主要是软件应用和更适合大众!
啥也不说的,这里能够认为这是一条脏数据
了。
再举个栗子
PHP中级研发工程师(ERP/MES方向)
一、计算机或相关学科本科或本科以上学历;
二、php和Java script的开发经验。
三、Linux和MySQL数据库的开发经验;
五、有ERP、MES相关开发经验优先;
六、英语的读写能力;
七、文化的开放性;
咱们提供
一、有趣的工做任务;
二、多元的工做领域;
三、与能力相关的收入;
四、年轻、开放并具备创造力的团队和工做氛围;
五、不断接触最新科技(尤为是工业4.0相关);
六、可适应短时间出差(提供差补);
这个只有要求,没职责,还多了个提供,我乐个趣 ╮(╯▽╰)╭
因此,气的想骂人。
[2017-12-19]更新
Boss直聘这里有部分招聘没有industryField、financeStage和companySize值,这个能够看前一篇的爬虫代码,拉勾网的数据基本没问题。
[2017-12-19] 更新
1年之内
,可是职位描述里倒是2年以上工做经验
,这是因为 HR 填写不规范引发的偏差。一年之内
,因此等级为2,可是薪水倒是20k-30k
,实际上这是等级为3的薪水,这里就得校订 level 字段,目前只是手动的把几个较高的记录手动改了,都校订过来很困难,得文本分析招聘要求。[2017-12-19] 更新
首先这里须要一个判断某条招聘是否还挂在网站上的方法,这个暂时想到了还没弄
而后对于发布时间在两个月以前的数据,就不进行统计计算
ok ,如今咱们的数据基本成这样了
{ "_id" : ObjectId("5a30ad2068504386f47d9a4b"), "city" : "苏州", "companyShortName" : "蓝海彤翔", "companySize" : "100-499人", "education" : "本科", "financeStage" : "B轮", "industryField" : "互联网", "level" : 3, "pid" : "11889834", "positionLables" : [ "PHP", "ThinkPHP" ], "positionName" : "php研发工程师", "salary" : { "avg" : 7500.0, "low" : 7000, "high" : 8000 }, "time" : "2017-06-06", "updated_at" : "2017-12-13 18:31:15", "workYear" : "1-3年", "detail" : "一、处理landcloud云计算相关系统的各种开发和调研工做;二、处理coms高性能计算的各种开发和调研工做岗位要求:一、本科学历,两年以上工做经验,熟悉PHP开发,了解经常使用的php开发技巧和框架;二、了解C++,python及Java开发;三、有必定的研发能力和钻研精神;四、有主动沟通能力和吃苦耐劳的精神。", "location" : "苏州市高新区科技城锦峰路158号101park8幢" }
因为还没到数据展现的时候,因此如今能想到的就是先这样处理了
项目开源地址:http://git.jtahstu.com/jtahst...
首先这个小玩意数据量并不够多,由于爬取时间短,站点惟一,再者广度局限在 PHP 这一个岗位上,以至存在必定的偏差。
因此为了数据的丰富和多样性,这个爬虫是必定要持续跑着的,至少要抓几个月的数据
才算可靠吧。
而后准备再去抓下拉勾网
的招聘数据,这也是个相对优秀的专业 IT 招聘网站了,数据也至关多,想当初找实习找正式工做,都是在这两个 APP 上找的,其余的网站几乎都没看。
最后,对于科班出身的学弟学妹们,过来人说一句,编程相关的职业就不要去志连、钱尘乌有、five eight桐城了,好吗?那里面都发的啥呀,看那些介绍内心没点数吗?
这里彻底就是做者本人依据我的微薄的见识,主观臆断作的一些事情,因此你们有什么点子和建议,均可以评论一下,多交流交流嘛。
后续会公开全部数据,你们本身能够本身分析分析。
咱们太年轻,以至都不知道之后的时光,居然那么长,长得足够让咱们把一门技术研究到顶峰,乱花渐欲迷人眼,请不要忘了根本好吗。
生活老是让咱们遍体鳞伤,但到后来,那些受伤的地方必定会变成咱们最强壮的地方。 —海明威 《永别了武器》