猎聘网数据分析职位的抓取与清洗

liepin.jpg

首先自我批评一下,上周出差没有更新博客,本周继续

上周突发奇想,想看看当前数据分析岗位的薪水情况。所以抓取了猎聘网20170427关于数据分析的所有100页内容。由于本人能力有限,整理笔记如下,以备后来翻阅。

数据抓取

数据抓取中遇到的困难

1. 本人爬虫功底有限,看了几页就照葫芦画瓢,代码能力不好
2. 猎聘网的安全防护阻止我100次循环连续抓取内容,所以只能连续20几页的抓取
3. 本打算获取技能要求的内容,但由于是动态加载,再加上时间有限,就没有过多的研究。所以在本章中没有实现抓取技能要求的内容。

爬虫代码

# -*- coding:utf-8 -*-
#__author__ = 'ecaoyng'

import urllib.request
import re
import csv
import time
import codecs

class LiePinCrawler:
    def __init__(self):
        self.user_agent = 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Mobile Safari/537.36'
        self.headers = {'User-Agent': self.user_agent}
        self.jobs=[]
        self.csv='C:/Users/ecaoyng/Desktop/work space/job.csv'

    # 获取搜索结果的某一页
    def getPage(self, pageIndex):
        try:
            url = 'https://www.liepin.com/zhaopin/?pubTime=&ckid=757c5403caae67a7&fromSearchBtn=2&compkind=&isAnalysis=&init=-1&searchType=1&dqs=&industryType=&jobKind=&sortFlag=15&industries=&salary=&compscale=&key=%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90&clean_condition=&headckid=757c5403caae67a7&curPage='
            url=url + str(pageIndex)
            # print(url)
            request = urllib.request.Request(url,headers = self.headers)
            response = urllib.request.urlopen(request)
            pageCode = response.read().decode('utf-8')
            print (pageCode)
            print('='*60)
            return pageCode
        except urllib.request.URLError as e:
            if hasattr(e, "reason"):
                print(u"Errors during connect to LiePin:", e.reason)
                return None
    #获取职位详细描述
    def getDesPage(self, contentURL):
        contentURL='https://www.liepin.com/job/198467615.shtml?imscid=R000000075&ckid=4704ed8e99af8c70&headckid=4704ed8e99af8c70&pageNo=0&pageIdx1&totalIdx=1&sup=1'
        print('contentURL is %s' % contentURL)
        request = urllib.request.Request(contentURL, headers=self.headers)
        response = urllib.request.urlopen(request)
        desPage = response.read().decode('utf-8')
        print(desPage)
        return desPage


    #加载一页并过滤需要的内容
    def getItems(self,pageIndex):
        pageCode=self.getPage(pageIndex)
        if not pageCode:
            print('页面加载失败...')
            return None
        pattern = re.compile(
            '<div class="job-info">.*?<h3 title.*?>.*?<a href="(.*?)".*?>(.*?)</a>.*?</h3>.*?<span class="text-warning">(.*?)</span>.*?<a href.*?>(.*?)</a>.*?<span class.*?>(.*?)</span>.*?<span>(.*?)</span>.*?<a title=.*?>(.*?)</a>.*?<a class.*?>(.*?)</a>.*?<p class=.*?>(.*?)</p>',
            re.S)
        items = re.findall(pattern, pageCode)


        for item in items:
            # contentPage=self.getDesPage(item[0].strip())
            # print(contentPage)
            break
            temp=re.sub('\s+','',item[8].strip())
            temp=re.sub('</span>','',temp)
            words=re.split(r'<span>',temp)
            self.jobs.append([item[0].strip(),item[1].strip(),item[2].strip(),item[3].strip(),item[4].strip(),item[5].strip(),item[6].strip(),item[7].strip(),words])
            print(self.jobs)
        return self.jobs



    #每次读取一页内容并写入文件
    def writeCSV(self):

        # for i in self.jobs:
        #     print(i)

        with open(self.csv, 'a+',encoding='utf-8') as csvFile:
            # csvFile.write(codecs.BOM_UTF8)
            csvwriter = csv.writer(csvFile, dialect=("excel"))
            # csvwriter.writerow(['Jobs','Salary','City','Edu','Years','Company','Industry','Key words'])
            for row in self.jobs:
                print(row)
                csvwriter.writerow(row)

        self.jobs=[]


if __name__== '__main__':

    liepin=LiePinCrawler()
    for i in range(0,20): # 可以修改此处,每次抓取指定页的内容
        time.sleep(5)
        liepin.getItems(i)
        #由于猎聘封号,所以只能读一页写一页
        liepin.writeCSV()

抓取的文件

由于不知该如何上传,所以无法展示供大家下载

抓取的文件放到excel中显示

由于抓取的文件是uft-8格式,放在excel中显示的中文乱码,但是在utraledit和notepad中可以正常打开。这是excel的问题,可以不用理睬,但如果非想用excel打开的话,解决方法如下.

1. 将excel后缀名改为txt 
2. 打开Excel-> Data->Get Data From Text
3. 打开txt,根据需求选择并next

数据清洗

数据分析中占用时间最多的就是数据清洗。
原始数据的特点:


1. 在年薪payment中会出现面议,保密等字样
2. 在年薪payment中出现13-20万这样的区间,不利于分析
3. 会有重复数据
5. 在place中有一行数据为空
4. 在地域中会出现类似北京、上海、深圳 这样的数据

针对如上的问题,解决思路如下:

1. 将保密替换为面议方便处理
2. 将一个13-20万这样的字段,拆分成三个income_min, income_max并计算income_avg
3. 丢弃掉重复数据
4. 在替他条件都整理好的情况下,将一条北京、上海、深圳这样的row,复制成三条数据并删除原有的行

下面来看下原始数据的情况

import pandas as pd
srcFile='C:\\Users\\ecaoyng\\Desktop\\work space\\job.csv'
#加入header=None是为了不把第一行数据方做header
liepin=pd.read_csv(srcFile,header=None)
liepin.columns=['jobs','payment','place','qualifications','experience','company','area','walfare']
# liepin.columns=['职位','年薪','工作地点','学历','工作年限','公司','行业','公司福利']
liepin.head()
jobs payment place qualifications experience company area walfare
0 数据分析主管(用户研究) 13-20万 上海 本科或以上 3年工作经验 资邦咨询 基金/证券/期货/投资 ['', '发展空间大', '技能培训', '岗位晋升', '五险一金', '绩效奖金', ...
1 数据分析岗(客服运营中心) 面议 上海 本科或以上 1年工作经验 深圳平安综合金融服务有限公司 保险 ['<spanclass="text-warning">12-18万<ahref="http...
2 数据分析经理 面议 上海 本科或以上 3年工作经验 善林(上海)金融 基金/证券/期货/投资 ['', '年底双薪', '绩效奖金', '带薪年假', '管理规范', '技能培训', '...
3 数据分析 5-7万 北京 本科或以上 经验不限 北京众信优联科技有限公司 互联网/移动互联网/电子商务 ['', '带薪年假', '午餐补助', '五险一金']
4 数据分析研究员 面议 北京 本科或以上 1年工作经验 360 互联网/移动互联网/电子商务 ['', '午餐补助', '绩效奖金', '五险一金', '节日礼物', '免费班车', '...
liepin.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3939 entries, 0 to 3938
Data columns (total 8 columns):
jobs              3939 non-null object
payment           3939 non-null object
place             3938 non-null object
qualifications    3939 non-null object
experience        3939 non-null object
company           3939 non-null object
area              3939 non-null object
walfare           3939 non-null object
dtypes: object(8)
memory usage: 246.3+ KB
#丢弃重复数据
liepin.drop_duplicates()

发现1646行的place为空,经查实应该是成都,填充上

#发现异常值在1646行
liepin.iloc[1646,2:3]='成都'
liepin.iloc[1646,:]

发现place中有类似上海-浦东区这样的内容,同意将其过滤为上海并添加一个城市字段

# 将city标准化
city=[]

for x in liepin.place:
    print(x)
    if '-' in x:
        x=x[0:x.index('-')]
    city.append(x)

liepin['City']=city
liepin.head(3)
jobs payment place qualifications experience company area walfare City
0 数据分析主管(用户研究) 13-20万 上海 本科或以上 3年工作经验 资邦咨询 基金/证券/期货/投资 ['', '发展空间大', '技能培训', '岗位晋升', '五险一金', '绩效奖金', . 上海
1 数据分析岗(客服运营中心) 面议 上海 本科或以上 1年工作经验 深圳平安综合金融服务有限公司 保险 ['<spanclass="text-warning">12-18万<ahref="http... 上海
2 数据分析经理 面议 上海 本科或以上 3年工作经验 善林(上海)金融 基金/证券/期货/投资 ['', '年底双薪', '绩效奖金', '带薪年假', '管理规范', '技能培训', '... 上海

按照之前所讲会出现多地址的问题


liepin.iloc[923,:]

Out[322]:
jobs                                                数据分析P6 p7 p8 p9
payment                                                      40-70万
place                                                     上海,北京,浙江省
qualifications                                                本科或以上
experience                                                   3年工作经验
company                                                   国内最大互联网企业
area                                                 互联网/移动互联网/电子商务
walfare           ['<iclass="icons24icons24-honesty"></i><em>该职位...
City                                                      上海,北京,浙江省
Name: 923, dtype: object

另外,本打算丢弃掉面议的row,但无奈太多,不能丢弃

liepin[liepin.payment=='面议'].info()

<class 'pandas.core.frame.DataFrame'>
Int64Index: 907 entries, 1 to 3938
Data columns (total 9 columns):
jobs              907 non-null object
payment           907 non-null object
place             907 non-null object
qualifications    907 non-null object
experience        907 non-null object
company           907 non-null object
area              907 non-null object
walfare           907 non-null object
City              907 non-null object
dtypes: object(9)
memory usage: 70.9+ KB

为了对薪资进行处理,根据年薪字段,增加三个薪水字段,分别为最低年薪,最高年薪,平均年薪

#新建三个地段来描述薪资情况
import re

income_avg=[]
income_min=[]
income_max=[]
for i in liepin.payment:
    if i.strip()== '面议':
        income_avg.append('面议')
        income_min.append('面议')
        income_max.append('面议')
    elif(i.strip()=='保密'):
        # 数据中有一行的薪水是保密,将其改为面议
        income_avg.append('面议')
        income_min.append('面议')
        income_max.append('面议')
    else:
        pattern=r'(\d+)-(\d+).*'
        regex=re.compile(pattern)
        m=regex.match(i)
        income_min.append(m.group(1))
        income_max.append(m.group(2))
        int_min=int(m.group(1))
        int_max=int(m.group(2))
        
        income_avg.append(format((int_min+int_max)/2,'.2f'))
        
liepin['income_min']=income_min
liepin['income_max']=income_max
liepin['income_avg']=income_avg

增加字段后的表结构如下

jobs payment place qualifications experience company area walfare City income_min income_max income_avg
0 数据分析主管(用户研究) 13-20万 上海 本科或以上 3年工作经验 资邦咨询 基金/证券/期货/投资 ['', '发展空间大', '技能培训', '岗位晋升', '五险一金', '绩效奖金', ... 上海 13 20 16.50
1 数据分析岗(客服运营中心) 面议 上海 本科或以上 1年工作经验 深圳平安综合金融服务有限公司 保险 ['<spanclass="text-warning">12-18万<ahref="http... 上海 面议 面议 面议
2 数据分析经理 面议 上海 本科或以上 3年工作经验 善林(上海)金融 基金/证券/期货/投资 ['', '年底双薪', '绩效奖金', '带薪年假', '管理规范', '技能培训', '... 上海 面议 面议 面议

现在表的整体信息是:

liepin.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3939 entries, 0 to 3938
Data columns (total 12 columns):
jobs              3939 non-null object
payment           3939 non-null object
place             3939 non-null object
qualifications    3939 non-null object
experience        3939 non-null object
company           3939 non-null object
area              3939 non-null object
walfare           3939 non-null object
City              3939 non-null object
income_min        3939 non-null object
income_max        3939 non-null object
income_avg        3939 non-null object
dtypes: object(12)
memory usage: 369.4+ KB

对多地址行进行复制

# 显示多地址行
liepin[liepin.place.str.contains(',')].head(2)
jobs payment place qualifications experience company area walfare City income_min income_max income_avg
827 数据分析工程师 20-40万 北京,上海,深圳 本科或以上 3年工作经验 全国知名的风电企业 能源(电力/水利) ['<iclass="icons24icons24-honesty"></i><em>该职位... 北京,上海,深圳 20 40 30.00
828 数据分析专员 10-15万 北京,上海,深圳 本科或以上 经验不限 全国知名的风电企业 能源(电力/水利) ['<iclass="icons24icons24-honesty"></i><em>该职位... 北京,上海,深圳 10 15 12.50
#一共159条数据,无法忽略
liepin[liepin.place.str.contains(',')].info()

#获取一行dataframe一行数据
liepin.iloc[[826]]
# 将一行数据拆分成三行
for i in liepin[liepin.place.str.contains(',')].index:
    for city in str(liepin.iloc[i].place).split(','):

        row=pd.DataFrame([dict(jobs=liepin.iloc[i].jobs,
        payment=liepin.iloc[i].payment,
        place=city, 
        qualifications=liepin.iloc[i].qualifications,
        experience=liepin.iloc[i].experience,
        company=liepin.iloc[i].company,
        area=liepin.iloc[i].area,
        walfare=liepin.iloc[i].walfare,
        City=city,
        income_min=liepin.iloc[i].income_min, 
        income_max=liepin.iloc[i].income_max,
        income_avg=liepin.iloc[i].income_avg,
        )])
        liepin=liepin.append(row,ignore_index=True)
    
liepin.tail()
City area company experience income_avg income_max income_min jobs payment place qualifications walfare
4364 武汉 百货/批发/零售 国内某知名百货公司 5年工作经验 22.50 30 15 数据分析经理 15-30万 武汉 本科或以上 ['<iclass="icons24icons24-honesty"></i><em>该职位...
4365 东莞 中介服务 任仕达公司 12年工作经验 75.00 90 60 制造大数据分析架构师 (56688) 60-90万 东莞 硕士或以上 ['<iclass="icons24icons24-honesty"></i><em>该职位...
4366 深圳 中介服务 任仕达公司 12年工作经验 75.00 90 60 制造大数据分析架构师 (56688) 60-90万 深圳 硕士或以上 ['<iclass="icons24icons24-honesty"></i><em>该职位...
#丢弃重复数据
liepin.drop_duplicates()
# 查看多地址行,一共159行
liepin[liepin.place.str.contains(',')].index.size

#丢弃掉多地址字段
for i in liepin[liepin.place.str.contains(',')].index:
      liepin.drop(i,inplace=True)
liepin.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4208 entries, 0 to 4366
Data columns (total 12 columns):
City              4208 non-null object
area              4208 non-null object
company           4208 non-null object
experience        4208 non-null object
income_avg        4208 non-null object
income_max        4208 non-null object
income_min        4208 non-null object
jobs              4208 non-null object
payment           4208 non-null object
place             4208 non-null object
qualifications    4208 non-null object
walfare           4208 non-null object
dtypes: object(12)
memory usage: 427.4+ KB
#check发现已经没有多地址的行
liepin[liepin.place.str.contains(',')].index.size # 0

#将其写入csv文件
targetDir='C:\\Users\\ecaoyng\\Downloads\\jobs_clean.csv'
liepin.to_csv(targetDir)

#数据清洗完成

将在下一篇中记录分析过程。
最后感谢 TigerDrFish成元

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容