入门小远学爬虫(二)(六)简单GET型网页爬虫实战——“前程无忧”爬虫岗位信息的爬取之简单“数据清洗”


前言

这是本系列第一个实战项目的第六课,有关前五课相关的内容请访问小远的主页。(上一课的链接

上一节我们已经利用xlsxwriter库将前程无忧爬虫岗位中前十页的工作的详细信息全部存入Excel文件,效果如图:
在这里插入图片描述
但是仔细看,数据还并不完美,主要表现在以下几个方面:
1、有的公司的位置精确到了市,而有的精确到了区,这样画图的时候不太方便(关于画图小远会在之后的文章推出,敬请期待)。我们需要获取工作地点的经纬度坐标才好。
2、工资那一栏,有的单位是“万/月”、有的是“千/月”、还有的是“元/天”,这样只看数值的话就很不好比较。
3、有的公司没有学历要求,这就使得他们学历那一栏为空,不好看
4、对后几栏做词频统计,为之后画饼状图打好铺垫
5、……

处理这些问题的操作,叫做数据清洗


一、关于数据清洗

数据清洗是指发现并纠正数据文件中可识别的错误的最后一道程序,包括检查数据一致性,处理无效值和缺失值等。与问卷审核不同,录入后的数据清理一般是由计算机而不是人工完成。——《百度百科》

数据清洗原理示意图
数据清洗原理示意图
篇幅有限,更重要的是博主技术有限,本节只会使用最最简单的数据清洗方法。

二、分别分析需求

1、用工作地点确定经纬度:

将所有的工作地点打印出来,有

['上海-徐汇区', '宁波-鄞州区', '长沙-岳麓区', '合肥-高新区', '昆明', '上海-闵行区',
 '广州-天河区', '北京-朝阳区', '武汉-江汉区', '上海-闵行区', '武汉', '苏州', '北京-海淀区', 
 ……
 '苏州-高新区', '无锡-惠山区', '重庆-渝北区', '深圳-南山区', '广州']

由于在比例尺不大的中国地图上,同一市级行政单位的各区级行政单位相距不远,再加之有些公司地点并未精确到区,所以我们只要能够剥离出城市名然后再进行查找比对就行了。这里小远分享一个资源,点击链接免费下载,这里面收录中国绝大多数地级市、县级市和县(包括自治县、旗、自治旗、特区和林区)的经纬度坐标信息,近2200余条
城市

我们的处理思路如下:
step1:将文件放在目录下,检测有无“-”字符
step2:如无,直接在json文件中检测;如有,取“-”之前的字符串(即为城市名)
step3:将所有经纬度以元组列表形式存储

那么我们可以用一个临时.py文件来测试思路,最后再加到主程序中

dic = json.load(open('城市经纬度.json', 'rb'))
LngLat = []
for city in cities:
    LngLat.append(tuple(dic[city.split('-')[0] if '-' in city else city]))
print(LngLat)

运行结果如图
经纬度

显然这个思路没有错,继续下一个:

2、工资待遇统一单位:

工资待遇一栏如下:

['1.2-1.6万/月', '0.8-1万/月', '0.8-1万/月', '5-7千/月', '0.8-1万/月', '1.5-2.5万/月',
 '0.8-1.2万/月', '1.5-2万/月', '1-1.5万/月', '6-8万/月', '1-1.5万/月', '0.5-1万/月', 
 ……
 '2-2.5万/月', '20-30万/年', '2-3千/月', '4-8千/月', '1-1.5万/月']

先分析一下这里工资数据的结构:
【A】-【B】(C)/(D)
A:最低工资
B:最高工资
C:货币单位
D:时间单位
那么统一单位就交给数学知识了,这里我需要将其统一成(千/月)并求一下平均工资

这里仍然需要用到正则表达式,关于正则的知识,可以参考小远的博文“入门小远学爬虫(二)(四)简单GET型网页爬虫实战——“前程无忧”爬虫岗位信息的爬取之正则概念以及Python中re库的简单应用
(有个别如下情形会使得异常,需特殊处理,具体见代码)
【A】(C)/(D)
比如:
异常
关于异常处理属Python基本语法,在此不赘述

直接上代码

for wage in wages:
    try:
        temp = re.findall(r'(.*)-(.*)([万/千/元])/([年/月/天/日])', wage)[0]
        wMin = eval(temp[0])
        wMax = eval(temp[1])
    except IndexError:
        temp = re.findall(r'(.+)(-?)([万/千/元])/([年/月/天/日])', wage)[0]
        wMin = eval(temp[0])
        wMax = wMin
    if temp[2] == '万':
        wMin, wMax = wMin * 10, wMax * 10
    elif temp[2] == '元':
        wMin, wMax = wMin / 1000, wMax / 1000
    if temp[3] == '年':
        wMin, wMax = wMin / 12, wMax / 12
    elif temp[3] == '天' or temp[3] == '日':
        wMin, wMax = wMin * 30, wMax * 30

    print("转化前{},{}-{}千/月,平均{}千/月".format(wage, format(wMin, '0.1f'), format(wMax, '0.1f'), format(0.5*(wMin+wMax), '0.1f')))

运行结果如图
工资

3、补全空格:

这一部分比较简单,直接在正式程序里写算了(第三大点,就在下面)

4、对后几列做词频统计

对于词频统计的方法,当然是用字典了
为了方便,小远直接假设一个数据,比如

['A', 'B', 'C', 'D', 'E', 'F', 'A', 'F', 'C', 'D']

现在我要对这个列表进行遍历并做词频统计,那么
上代码:

a = ['A', 'B', 'C', 'D', 'E', 'F', 'A', 'F', 'C', 'D']
dic = {}
for word in a:
    dic[word] = 1 if word not in dic else dic[word] + 1
print(dic)

运行结果如图
词频统计

当然,真实数据当然不止这么一点,那么,重头戏来了

三、修改完善主程序

# -*- coding: utf-8 -*-
# @Time: 2020/11/29 14:21
# @Author: 胡志远
# @Software: PyCharm

# 导入re包
import re
# 导入requests包
import requests
# 导入lxml中的html模块
from lxml import html
# 导入xlsxwriter包
import xlsxwriter
# 需要用到时间
import datetime

import json

# 请求头
headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Connection": "keep-alive",
    "Cookie": "guid=7e8a970a750a4e74ce237e74ba72856b; partner=blog_csdn_net",
    "Host": "jobs.51job.com",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36"
}
Information = list()
for i in range(1, 11):
    # 网页链接
    url = "https://jobs.51job.com/pachongkaifa/p{}/".format(i)
    # 有请求头写法
    res = requests.get(url=url, headers=headers)
    res.encoding = "gbk"
    # 生成Element对象
    etree = html.etree
    html_elements = etree.HTML(res.text)
    # 根据Xpath路径获取每一个工作的对象
    Jobs_element = html_elements.xpath('//div[@class="detlist gbox"]/div')
    # 正则
    regex = '.*href="(.*)">(.*)</a.*href=.*">(.*)</a.*me">(.*)</span.*on">(.*)</span.*me">(.*)</span.*:(.*)<s.*:(.*)<s.*:(.*)<s.*:(.*)</p.*title='
    for aJob in Jobs_element:
        Information += re.findall(regex, etree.tostring(aJob, encoding="utf-8").decode(), re.S)
    # 将元组列表改为列表列表
    Information = [list(tup) for tup in Information]


# 对数据进行处理

def wage(w):
    try:
        temp = re.findall(r'(.*)-(.*)([万/千/元])/([年/月/天/日])', w)[0]
        wMin = eval(temp[0])
        wMax = eval(temp[1])
    except IndexError:
        temp = re.findall(r'(.+)(-?)([万/千/元])/([年/月/天/日])', w)[0]
        wMin = eval(temp[0])
        wMax = wMin
    if temp[2] == '万':
        wMin, wMax = wMin * 10, wMax * 10
    elif temp[2] == '元':
        wMin, wMax = wMin / 1000, wMax / 1000
    if temp[3] == '年':
        wMin, wMax = wMin / 12, wMax / 12
    elif temp[3] == '天' or temp[3] == '日':
        wMin, wMax = wMin * 30, wMax * 30
    return "{}-{}千/月".format(format(wMin, '0.1f'), format(wMax, '0.1f')), "{}千/月".format(0.5 * (wMin + wMax), '0.1f')


j = open('城市经纬度.json', 'rb')
city = json.load(j)
# 创建四个空字典
Education, Experience, Type, Count = {}, {}, {}, {}
for item in Information:
    # 将经纬度坐标加在后面
    item.append(str(tuple(city[item[3].split('-')[0] if '-' in item[3] else item[3]])))
    # 将工资统一格式化并将平均工资加在后面
    wg, aveWg = wage(item[4])
    item[4] = wg
    item.append(aveWg)
    # 若学历信息为空改为无学历要求
    item[6] = item[6] if item[6] != "" else "无学历要求"
    # 对学历要求、需要资历、公司类型、招聘人数进行词频统计
    Education[item[6]] = 1 if item[6] not in Education else Education[item[6]] + 1
    Experience[item[7]] = 1 if item[7] not in Experience else Experience[item[7]] + 1
    Type[item[8]] = 1 if item[8] not in Type else Type[item[8]] + 1
    Count[item[9]] = 1 if item[9] not in Count else Count[item[9]] + 1
j.close()

wb = xlsxwriter.Workbook('前程无忧招聘信息.xlsx')
ws = wb.add_worksheet('爬虫岗位')
# 先打印一个时间
ws.write(0, 0, "数据截止时间", wb.add_format({'bold': True}))  # 加粗
ws.write(0, 1, datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S'))
# 再写上列名
columnNames = ("网址", "岗位名称", "所属公司", "工作地点", "工资待遇", "招聘信息发布时间", "学历要求", "需要资历", "公司类型", "招聘人数", "工作地点经纬度", "平均工资")
for columnName in columnNames:
    ws.write(1, columnNames.index(columnName), columnName, wb.add_format({'bold': True}))
# 查看总列表
for item in Information:
    for each in item:
        ws.write(Information.index(item) + 2, item.index(each), each)
wb.close()

# 暂时先将四个字典打印出来,之后还有用的
print(Education)
print(Experience)
print(Type)
print(Count)

运行结果截图
结果
.xlsx文件截图
在这里插入图片描述

下一节已更新


如果觉得博主写的还不错的,欢迎点赞、评论、加关注,大家的访问就是博主更新文章不竭的源动力!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

远哥挺乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值