Pyhton爬蟲實戰 - 抓取BOSS直聘職位描述 和 數據清洗

Pyhton爬蟲實戰 - 抓取BOSS直聘職位描述 和 數據清洗

零、致謝

感謝BOSS直聘相對權威的招聘信息,使本人有了這次比較有意思的研究之旅。

由於爬蟲持續爬取 www.zhipin.com 網站,以致產生的服務器壓力,本人深感歉意,並沒有 DDoS 和危害貴網站的意思。

2017-12-14 更新
在跑了一夜之後,服務器 IP 還是被封了,搞得本人現在家裏、公司、雲服務器三線作戰啊

一、抓取詳細的職位描述信息

1.1 前提數據

這裏需要知道頁面的 id 才能生成詳細的鏈接,在 Python爬蟲框架Scrapy實戰 - 抓取BOSS直聘招聘信息 中,我們已經拿到招聘信息的大部分信息,裏面有個 pid 字段就是用來唯一區分某條招聘,並用來拼湊詳細鏈接的。

是吧,明眼人一眼就看出來了。


1.2 詳情頁分析

詳情頁如下圖所示

P2

在詳情頁中,比較重要的就是職位描述工作地址這兩個

由於在頁面代碼中崗位職責任職要求是在一個 div 中的,所以在抓的時候就不太好分,後續需要把這個連體嬰兒,分開分析。


1.3 爬蟲用到的庫

使用的庫有

  • requests
  • BeautifulSoup4
  • pymongo

對應的安裝文檔依次如下,就不細說了


1.4 Python 代碼

"""
@author: jtahstu
@contact: [email protected]
@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,初學者都能看懂。


1.5 再囉嗦幾句

上一篇文章 中只是爬了 上海-PHP 近300條數據,後續改了代碼,把12個城市的 PHP 相關崗位的數據都抓下來了,有3500+條數據,慢慢爬吧,急不來。

像這樣
P7
P8

二、數據清洗

2.1 校正發佈日期

"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')

2.2 校正薪水以數字保存

"salary" : "5K-12K",

#處理成下面的格式
"salary" : {
    "low" : 5000,
    "high" : 12000,
    "avg" : 8500.0
},
# 薪水處理成數字
def clear_salary():
    items = db.jobs_php.find({})
    for item in items:
        if type(item['salary']) == type({}):
            continue
        salary_list = item['salary'].replace("K", "000").split("-")
        salary_list = [int(x) for x in salary_list]
        item['salary'] = {
            'low': salary_list[0],
            'high': salary_list[1],
            'avg': (salary_list[0] + salary_list[1]) / 2
        }
        update(item)
    print('ok')

2.3 根據 工作經驗年限 劃分招聘等級

# 設置招聘的水平
def set_level():
    items = db.jobs_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年左右,但是還是建議捨棄掉。


2.4 區分開<崗位職責>和<任職要求>

對於作者這個初學者來說,這裏還沒有什麼好的方法,知道的同學,請務必聯繫作者,聯繫方式在個人博客

so , i’m sorry.

爲什麼這兩個不好劃分出來呢?

因爲這裏填的並不統一,可以說各種花樣,有的要求在前,職責在後,有的又換個名字區分。目前看到的關於要求的有['任職條件', '技術要求', '任職要求', '任職資格', '崗位要求']這麼多說法。然後順序還不一樣,有的要求在前,職責在後,有的又反之。

舉個栗子

會基本的php編程!能夠修改簡單的軟件!對雲服務器和數據庫能夠運用!懂得微信公衆賬號對接和開放平臺對接!我們不是軟件公司,是運營公司!想找好的公司學習的陝西基本沒有,要到沿海城市去!但是我們是實用型公司,主要是軟件應用和更適合大衆!

啥也不說的,這裏可以認爲這是一條髒數據了。

不行,再舉個栗子

PHP中級研發工程師(ERP/MES方向)
1、計算機或相關學科本科或本科以上學歷;
2、php和Java script的開發經驗。
3、Linux和MySQL數據庫的開發經驗;
5、有ERP、MES相關開發經驗優先;
6、英語的讀寫能力;
7、文化的開放性;
我們提供
1、有趣的工作任務;
2、多元的工作領域;
3、與能力相關的收入;
4、年輕、開放並具有創造力的團隊和工作氛圍;
5、不斷接觸最新科技(尤其是工業4.0相關);
6、可適應短期出差(提供差補);

這個只有要求,沒職責,還多了個提供,我樂個趣 ╮(╯▽╰)╭

所以,氣的想罵人。


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" : "1、處理landcloud雲計算相關係統的各類開發和調研工作;2、處理coms高性能計算的各類開發和調研工作崗位要求:1、本科學歷,兩年以上工作經驗,熟悉PHP開發,瞭解常用的php開發技巧和框架;2、瞭解C++,python及Java開發;3、有一定的研發能力和鑽研精神;4、有主動溝通能力和吃苦耐勞的精神。",
    "location" : "蘇州市高新區科技城錦峯路158號101park8幢"
}

由於還沒到數據展示的時候,所以現在能想到的就是先這樣處理了

項目開源地址:http://git.jtahstu.com/jtahstu/Scrapy_zhipin

三、展望和設想

首先這個小玩意數據量並不夠多,因爲爬取時間短,站點唯一,再者廣度侷限在 PHP 這一個崗位上,以致存在一定的誤差。

所以爲了數據的豐富和多樣性,這個爬蟲是一定要持續跑着的,至少要抓幾個月的數據纔算可靠吧。

然後準備再去抓下拉勾網的招聘數據,這也是個相對優秀的專業 IT 招聘網站了,數據也相當多,想當初找實習找正式工作,都是在這兩個 APP 上找的,其他的網站幾乎都沒看。

最後,對於科班出身的學弟學妹們,過來人說一句,編程相關的職業就不要去志連、錢塵烏有、five eight桐城了,好嗎?那裏面都發的啥呀,看那些介紹心裏沒點數嗎?

四、help

這裏完全就是作者本人依據個人微薄的見識,主觀臆斷做的一些事情,所以大家有什麼點子和建議,都可以聯繫作者,多交流交流嘛。

後續會公開所有數據,大家自己可以弄着玩玩吧。

我們太年輕,以致都不知道以後的時光,竟然那麼長,長得足夠讓我們把一門技術研究到頂峯,亂花漸欲迷人眼,請不要忘了根本好嗎。

生活總是讓我們遍體鱗傷,但到後來,那些受傷的地方一定會變成我們最強壯的地方。 —海明威 《永別了武器》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章