Python爬虫实战 - 抓取BOSS直聘职位描述 和 数据清洗!

Python爬虫实战 - 抓取BOSS直聘职位描述 和 数据清洗!,第1张

概述<h2style=\"margin-top:0px;margin-bottom:0px;padding:0px;font-weight:400;font-size:22px;max-width:100%;font-family:\'-apple-system-font\',BlinkMacSystemFont,\'HelveticaNeue\',\'PingFangSC\',\'H

<h2 ><span ><span >零、致谢<p >感谢BOSS直聘相对权威的招聘信息,使本人有了这次比较有意思的研究之旅。

<p >由于爬虫持续爬取 www.zhipin.com 网站,以致产生的服务器压力,本人深感歉意,并没有 DDoS 和危害贵网站的意思。

<blockquote >2017-12-14 更新
在跑了一夜之后,服务器 IP 还是被封了,搞得本人现在家里、公司、云服务器三线作战啊<p ><span ><span ><span >一、抓取详细的职位描述信息

<p >

<h3 ><span ><span >1.1 前提数据<p >这里需要知道页面的 ID 才能生成详细的链接,在 文章《<span >Python爬虫框架Scrapy实战 - 抓取BOSS直聘招聘信息》 (<span >链接:http://www.jtahstu.com/blog/scrapy_zhipin_spIDer.HTML)中,我们已经拿到招聘信息的大部分信息,里面有个 <code >pID 字段就是用来唯一区分某条招聘,并用来拼凑详细链接的。

<p >是吧,明眼人一眼就看出来了。

<h3 >1.2 详情页分析<p >

<p ><span >详情页如下图所示

<p ><span >

<p >

<p >

<p >在详情页中,比较重要的就是<code >职位描述和<code >工作地址这两个

<p >由于在页面代码中<code >岗位职责和<code >任职要求是在一个 div 中的,所以在抓的时候就不太好分,后续需要把这个连体婴儿,分开分析。

<h3 ><span ><span >1.3 爬虫用到的库<p >

<ul ><li ><p ><span ><span >requests<span >

<li ><p ><span >BeautifulSoup4

<li ><p >pymongo

<p >对应的安装文档依次如下,就不细说了

<ul ><li ><p >安装 Requests - Requests 2.18.1 文档(http://docs.python-requests.org/zh_CN/latest/user/install.HTML)<span >

<li ><p ><span >安装 Beautiful Soup - Beautiful Soup 4.2.0 文档(https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.HTML#ID5)

<li ><p >PyMongo安装使用笔记(http://www.jb51.cc/article/64996.htm)

<p >1.4 Python 代码<pre ><code ><span >"""
@author: jtahstu
@contact: root@jtahstu.com
@site: http://www.jtahstu.com
@time: 2017/12/10 00:25
"""
<span ># -- Coding: utf-8 --
<span >import requests
<span >from bs4 <span >import BeautifulSoup
<span >import time
<span >from pymongo <span >import MongoClIEnt

headers = {
   <span >'x-devtools-emulate-network-conditions-clIEnt-ID': <span >"5f2fc4da-c727-43c0-aad4-37fce8e3ff39",
   <span >'upgrade-insecure-requests': <span >"1",116);">'user-agent': <span >"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",116);">'accept': <span >"text/HTML,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8",116);">'dnt': <span >'accept-enCoding': <span >"gzip,deflate",116);">'accept-language': <span >"zh-CN,zh;q=0.8,en;q=0.6",116);">'cookie': <span >"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",116);">'cache-control': <span >"no-cache",116);">'postman-token': <span >"76554687-c4df-0c17-7cc0-5bf3845c9831"
}
conn = MongoClIEnt(<span >'127.0.0.1',<span >27017)
db = conn.iApp  <span ># 连接mydb数据库,没有则自动创建

<span ><span >def <span >init<span >():
   items = db.jobs_PHP.find().sort(<span >'pID')
   <span >for item <span >in items:
       <span >if <span >'detail' <span >in item.keys(): <span ># 在爬虫挂掉再此爬取时,跳过已爬取的行
           <span >continue
       detail_url = <span >"https://www.zhipin.com/job_detail/%s.HTML?ka=search_List_1" % item[<span >'pID']
       print(detail_url)
       HTML = requests.get(detail_url,headers=headers)
       <span >if HTML.status_code != <span >200: <span ># 爬的太快网站返回403,这时等待解封吧
           print(<span >'status_code is %d' % HTML.status_code)
           <span >break
       soup = BeautifulSoup(HTML.text,<span >"HTML.parser")
       job = soup.select(<span >".job-sec .text")
       <span >if len(job) < <span >1:
           <span >continue
       item[<span >'detail'] = job[<span >0].text.strip()  <span ># 职位描述
       location = soup.select(<span >".job-sec .job-location")
       item[<span >'location'] = location[<span ># 工作地点
       item[<span >'updated_at'] = time.strftime(<span >"%Y-%m-%d %H:%M:%s",time.localtime())  <span ># 实时爬取时间
       res = save(item) <span ># 保存数据
       print(res)
       time.sleep(<span >40) <span ># 停停停

<span ># 保存数据到 MongoDB 中
<span >save<span >(item):
   <span >return db.jobs_PHP.update_one({<span >"_ID": item[<span >'_ID']},{<span >"$set": item})

<span >if name == <span >"main":
   init()<p ><span >代码 easy,初学者都能看懂。

<h3 ><span ><span ><span >1.5 再啰嗦几句<p >在<span >文章《Python爬虫框架Scrapy实战 - 抓取BOSS直聘招聘信息》 (链接:http://www.jtahstu.com/blog/scrapy_zhipin_spIDer.HTML)中,只是爬了 <code >上海-PHP<span > 近300条数据,后续改了代码,把12个城市的 PHP 相关岗位的数据都抓下来了,有3500+条数据,慢慢爬吧,急不来。

<p >像这样

<p ><span >

<p >

<p >

<p >二、数据清洗

<p >2.1 校正发布日期<pre >"time" : <span >"发布于03月31日",
<span >"发布于昨天",116);">"发布于11:31",<p ><span >这里拿到的都是这种格式的,所以简单处理下

<pre ><span >import datetime
<span >import MongoClIEnt
db = MongoClIEnt(<span >27017).iApp
<span >update<span >(data):
   <span >"_ID": data[<span >"$set": data})
<span ># 把时间校正过来
<span >clear_time<span >():
   items = db.jobs_PHP.find({})
   <span >if <span >not item[<span >'time'].find(<span >'布于'):
           <span >'time'] = item[<span >'time'].replace(<span >"发布于",116);">"2017-")
       item[<span >"月",116);">"-")
       item[<span >"日",116);">"")
       <span >if item[<span >"昨天") > <span >0:
           item[<span >'time'] = str(datetime.date.today() - datetime.timedelta(days=<span >1))
       <span >elif item[<span >":") > <span >'time'] = str(datetime.date.today())
       update(item)
   print(<span >'ok')<h3 ><span ><span >2.2 校正薪水以数字保存<pre >"salary" : <span >"5K-12K",

<span >#处理成下面的格式
<span >"salary" : {
   <span >"low" : <span >5000,116);">"high" : <span >12000,116);">"avg" : <span >8500.0
},<pre ><span ># 薪水处理成数字
<span >clear_salary<span >if type(item[<span >'salary']) == type({}):
           <span >continue
       salary_List = item[<span >'salary'].replace(<span >"K",116);">"000").split(<span >"-")
       salary_List = [int(x) <span >for x <span >in salary_List]
       item[<span >'salary'] = {
           <span >'low': salary_List[<span >0],
           <span >'high': salary_List[<span >1],116);">'avg': (salary_List[<span >0] + salary_List[<span >1]) / <span >2
       }
       update(item)
   print(<span >'ok')
<h3 >2.3 根据 工作经验年限 划分招聘等级<pre ># 设置招聘的水平
<span >set_level<span >'workYear'] == <span >'应届生':
           item[<span >'level'] = <span >1
       <span >'1年以内':
           item[<span >2
       <span >'1-3年':
           item[<span >3
       <span >'3-5年':
           item[<span >4
       <span >'5-10年':
           item[<span >5
       <span >'10年以上':
           item[<span >6
       <span >'经验不限':
           item[<span >10
       update(item)
   print(<span >'ok')<p ><span >

<p >这里有点坑的就是,一般要求<code >经验不限的岗位,需求基本都写在<code >任职要求里了,所以为了统计的准确性,这个等级的数据,后面会被舍弃掉。

<blockquote >2017-12-14 更新:
从后续的平均数据来看,这里的<code >经验不限,一般要求的是<code >1-3年左右,但是还是建议舍弃掉。<p ><span >

<h3 ><span ><span >2.4 区分开<岗位职责>和<任职要求><p ><span >对于作者这个初学者来说,这里还没有什么好的方法,知道的同学,请务必联系作者,联系方式在个人博客里

<p >so,i'm sorry.

<p ><span >为什么这两个不好划分出来呢?

<p >因为这里填的并不统一,可以说各种花样,有的要求在前,职责在后,有的又换个名字区分。目前看到的关于要求的有<code >['任职条件','技术要求','任职要求','任职资格','岗位要求']这么多说法。然后顺序还不一样,有的要求在前,职责在后,有的又反之。

<p >举个栗子

<blockquote >会基本的PHP编程!能够修改简单的软件!对云服务器和数据库能够运用!懂得微信公众账号对接和开放平台对接!我们不是软件公司,是运营公司!想找好的公司学习的陕西基本没有,要到沿海城市去!但是我们是实用型公司,主要是软件应用和更适合大众!<p >啥也不说的,这里可以认为这是一条<code >脏数据了。

<p >不行,再举个栗子

<blockquote >PHP中级研发工程师(ERP/MES方向)
1、计算机或相关学科本科或本科以上学历;
2、PHP和Java script的开发经验。
3、linux和MysqL数据库的开发经验;
5、有ERP、MES相关开发经验优先;
6、英语的读写能力;
7、文化的开放性;
我们提供
1、有趣的工作任务;
2、多元的工作领域;
3、与能力相关的收入;
4、年轻、开放并具有创造力的团队和工作氛围;
5、不断接触最新科技(尤其是工业4.0相关);
6、可适应短期出差(提供差补);<p >这个只有要求,没职责,还多了个提供,我乐个趣 ╮(╯▽╰)╭

<p >所以,气的想骂人。

<p ><span >ok ,现在我们的数据基本成这样了

<pre >{
   <span >"_ID" : ObjectID(<span >"5a30ad2068504386f47d9a4b"),116);">"city" : <span >"苏州",116);">"companyShortname" : <span >"蓝海彤翔",116);">"companySize" : <span >"100-499人",116);">"education" : <span >"本科",116);">"financeStage" : <span >"B轮",116);">"industryFIEld" : <span >"互联网",116);">"level" : <span >3,116);">"pID" : <span >"11889834",116);">"positionLables" : [
       <span >"PHP",
       <span >"ThinkPHP"
   ],116);">"positionname" : <span >"PHP研发工程师",116);">"salary" : {
       <span >7500.0,255);">7000,255);">8000
   },116);">"2017-06-06",116);">"updated_at" : <span >"2017-12-13 18:31:15",116);">"workYear" : <span >"1-3年",116);">"detail" : <span >"1、处理landcloud云计算相关系统的各类开发和调研工作;2、处理coms高性能计算的各类开发和调研工作岗位要求:1、本科学历,两年以上工作经验,熟悉PHP开发,了解常用的PHP开发技巧和框架;2、了解C++,python及Java开发;3、有一定的研发能力和钻研精神;4、有主动沟通能力和吃苦耐劳的精神。",116);">"location" : <span >"苏州市高新区科技城锦峰路158号101park8幢"
}<p >由于还没到数据展示的时候,所以现在能想到的就是先这样处理了。

<p >项目开源地址:http://git.jtahstu.com/jtahstu/Scrapy_zhipin

<p >三、展望和设想

<p >首先这个小玩意数据量并不够多,因为爬取时间短,站点唯一,再者广度局限在 PHP 这一个岗位上,以致存在一定的误差。

<p >所以为了数据的丰富和多样性,这个爬虫是一定要持续跑着的,至少要<code >抓几个月的数据才算可靠吧。

<p >然后准备再去抓下<code >拉勾网的招聘数据,这也是个相对优秀的专业 IT 招聘网站了,数据也相当多,想当初找实习找正式工作,都是在这两个 APP 上找的,其他的网站几乎都没看。

<p >最后,对于科班出身的学弟学妹们,过来人说一句,编程相关的职业就不要去志连、钱尘乌有、five eight桐城了,好吗?那里面都发的啥呀,看那些介绍心里没点数吗?

<p ><span ><span >四、help

<p >这里完全就是作者本人依据个人微薄的见识,主观臆断做的一些事情,所以大家有什么点子和建议,都可以联系作者,多交流交流嘛。

<p >后续会公开所有数据,大家自己可以弄着玩玩吧。

<p >我们太年轻,以致都不知道以后的时光,竟然那么长,长得足够让我们把一门技术研究到顶峰,乱花渐欲迷人眼,请不要忘了根本好吗。

<p >进群:125240963   即可获取源码!

<blockquote >生活总是让我们遍体鳞伤,但到后来,那些受伤的地方一定会变成我们最强壮的地方。 —海明威 《永别了武器》

总结

以上是内存溢出为你收集整理的Python爬虫实战 - 抓取BOSS直聘职位描述 和 数据清洗!全部内容,希望文章能够帮你解决Python爬虫实战 - 抓取BOSS直聘职位描述 和 数据清洗!所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1209002.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)

保存