程序员数据乐园@IT·互联网

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

2017-05-11  本文已影响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成元

上一篇下一篇

猜你喜欢

热点阅读