美文网首页程序员数据乐园@IT·互联网
猎聘网数据分析职位的抓取与清洗

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

作者: 海墨星人 | 来源:发表于2017-05-11 18:46 被阅读0次
    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成元

    相关文章

      网友评论

        本文标题:猎聘网数据分析职位的抓取与清洗

        本文链接:https://www.haomeiwen.com/subject/lhzotxtx.html