数据挖掘——数据采集和数据清洗

本篇博客介绍了数据清洗中的数据去重和处理空值的方法,包括使用pandas的drop_duplicates()函数去除重复行以及fillna()函数填充空值。此外,还讲解了数据采集的基础知识,如网页爬取的步骤、动态载入和解析,以及如何应对反爬策略。提供了实例代码,包括从CSV文件中读取数据、去重、填充空值,以及爬取网页标题并保存为CSV文件。
摘要由CSDN通过智能技术生成

一、数据清洗

1.数据去重

(一)相关知识

1> pandas读取csv文件-read_csv()

    CSV为简单的文本格式文件,每行为一条用逗号分隔的数据。Excel保存文件时可以选择csv格式;在记事本里输入内容,保存时使用.csv扩展名,也能生成CSV文件。
    只要能阅读文本文件的编辑器,就能打开CSV文件,也可以用Excel把它作为电子表格打开。现在,我们要用pandas中的read_csv函数读取csv文件中的数据。这个函数有很多参数,在本关卡和接下来的关卡中,我们将逐个介绍其中几个常用的参数。
在本关卡中,我们只需要将CSV文件的文件名作为参数即可。

import pandas as pd
a=pd.read_csv('示例数据.csv')
#此时,a就存储了示例数据.csv文件中的数据
print(a)

# 输出结果如下:
#     1  2
# 0   3  4
# 1   5  6
# 2   1  3
# 3   1  2
# 4   4  2
2> pandas的去重函数-drop_duplicates()

pandas中的drop_duplicates()函数是对DataFrame格式的数据,可以去掉特定列的重复行。

drop_duplicates()有以下参数:
subset=
用于指定需要去重的列。默认为所有列。

keep:{‘first’, ‘last’, False}. default 'first’
删除重复项时是否保留其中第一次/最后一次出现的项。keep=False时不保留重复项;默认保留第一次出现的项。

inplace: boolean, default False
是否保留被修改的数据副本。默认不保留副本。

示例代码1

result_1=a.drop_duplicates()
# 删除a中a、b列均重复的数据;保留第一次出现的重复数据;保留副本
print(result_1)

# 输出结果如下:
#    a  b
# 0  3  4
# 1  5  6
# 2  3  1
# 3  1  2
# 4  4  2

示例代码2

result_2=a.drop_duplicates(subset='b', inplace=True)
# 删除a中b列重复的数据;保留第一次出现的重复数据;不保留副本
print(result_2)
# 输出结果为None.因为直接在a上修改,不再将修改结果传递给result_2
print(a)

# 输出结果如下:
#    a  b
# 0  3  4
# 1  5  6
# 2  3  1
# 3  1  2

(二)本关任务

利用pandas将‘basketball.csv’中的数据导入名为dataset的DataFrame对象中,并使用drop_duplicates()函数对dataset中的Date列进行去重,不保留副本。将去重结果dealed和dataset返回。

(三)参考代码

import pandas as pd


def duplicate():
    '''
    返回值:
    dealed: 对dataset的Date列进行去重后的DataFrame类型数据,不保留副本
    dataset: 初始读入的basketball.csv的数据
    '''
    # 请在此添加代码 完成本关任务
    # ********** Begin *********#
    dataset = pd.read_csv('basketball.csv')
    dealed = dataset.drop_duplicates(subset='Date')

    # ********** End **********#

    # 返回dealed,dataset
    return dealed, dataset

2.处理空值

(一)相关知识

1> DataFrame中空值的表示

    Pandas通常把空值表示为 NaN,以上一关卡中的basketball.csv为例,当中的OT?字段和Notes字段中就有许多空值。我们可以取出数据的前五行的OT?字段看一下DataFrame中空值的表示。

import pandas as pd
dataset=pd.read_csv('basketball.csv')
print(dataset["OT?"].ix[:5])

# 输出结果为:
0    NaN
1    NaN
2    NaN
3    NaN
4    NaN
5    NaN
Name: OT?, dtype: object

在数据挖掘过程中,模型通常不能直接接收NaN数据。这就需要我们对空值进行处理。在处理空值之前,要先能找出空值的个数和位置。

2> 查找空值及计算空值的个数

    Pandas中查找数据表中空值的函数有两个,一个是函数isnull,如果是空值就显示True。另一个函数notnull正好相反,如果是空值就显示False。
    分别利用这两个函数来找出basketball.csv数据中OT?字段和Notes字段中的个数。下面的一段程序是对isnull函数的使用示例。

print(dataset[:5].isnull())

# 输出结果为:
    Date Score Type Visitor Team VisitorPts Home Team HomePts   OT? Notes
0  False      False        False      False     False   False  True  True
1  False      False        False      False     False   False  True  True
2  False      False        False      False     False   False  True  True
3  False      False        False      False     False   False  True  True
4  False      False        False      False     False   False  True  True

    从输出的结果上来看,前五行数据只有OT?和Notes字段值是空值,其他均为非空。notnull函数的使用同理,只是输出的结果与isnull函数相反。

    接下来我们可能还需要计算数据中空值的个数。为了解决这个问题,我们可以使用value_counts函数。这个函数的使用示例见下:

result=dataset['OT?'].isnull()
#将dataset的OT?字段的空值与否情况传给result
print(result.value_counts())

# 输出结果为:
True     1151
False      79
Name: OT?, dtype: int64

由输出结果可以看出dataset中的OT?字段有1151个空值和79个非空值。

3> 处理空值-fillna

fillna()函数可以将空值位置填充任意指定的值。
下面这段代码将OT?字段中的空值赋值为’0OT’,将Notes字段中的空值赋值为’Nowhere’.

dataset=dataset['OT?'].fillna('0OT')
dataset=dataset['Notes'].fillna('Nowhere')
#在处理空值之后,我们可以打印出前五行数据,看看原先是'NaN'的位置现在的值是什么
print(dataset.ix[:5])

# 输出结果为:

              Date Score Type          Visitor Team  VisitorPts  \
0  Tue Oct 29 2013  Box Score         Orlando Magic          87
1  Tue Oct 29 2013  Box Score  Los Angeles Clippers         103
2  Tue Oct 29 2013  Box Score         Chicago Bulls          95
3  Wed Oct 30 2013  Box Score         Brooklyn Nets          94
4  Wed Oct 30 2013  Box Score         Atlanta Hawks         109
             Home Team  HomePts  OT?    Notes
0       Indiana Pacers       97  0OT  Nowhere
1   Los Angeles Lakers      116  0OT  Nowhere
2           Miami Heat      107  0OT  Nowhere
3  Cleveland Cavaliers       98  0OT  Nowhere
4     Dallas Mavericks      118  0OT  Nowhere

(二)本关任务

在本关卡中,我们为你提供了一个鸢尾花的数据集。数据集总共有五个字段,分别是:

  1. sepal.length
  2. sepal.width
  3. petal.length
  4. petal.width
  5. variety

除了variety字段内容是字符串外,其他字段的内容均是浮点数。你要完成的任务包括:

  • 找出每项字段中各有多少空值,并按上文给出的字段顺序打印出来。
  • 前四项字段的空值,各用该空值所在列的均值填充。
  • 第五项字段的空值,统一填充为’Unknown’.
  • 处理均值后的数据命名为dealed_data,并返回

注:pandas中均值的计算可以使用means()函数。
示例:data[‘a’].means()的结果就是a字段的均值。

(三)参考代码

import pandas as pd

dataset = pd.read_csv('iris.csv')


def count_nan(dataset):
    '''
    返回值:
    [n1,n2,n3,n4,n5]
    分别代表dataset的五列各有多少空值
    '''
    # 请在此添加代码 完成本关任务
    # ********** Begin *********#
    [n1, n2, n3, n4, n5] = dataset.isnull().sum()

    # ********** End **********#

    # 返回[n1,n2,n3,n4,n5]
    return [n1, n2, n3, n4, n5]


def deal_nan(dataset):
    '''
    返回值:
    dataset:按要求处理过、已经没有空值的数据
    '''
    # 请在此添加代码 完成本关任务
    # ********** Begin *********#
    dataset['sepal.length'] = dataset['sepal.length'].fillna(
        dataset['sepal.length'].mean())
    dataset['sepal.width'] = dataset['sepal.width'].fillna(
        dataset['sepal.width'].mean())
    dataset['petal.length'] = dataset['petal.length'].fillna(
        dataset['petal.length'].mean())
    dataset['petal.width'] = dataset['petal.width'].fillna(
        dataset['petal.width'].mean())
    dataset['variety'] = dataset['variety'].fillna('Unknown')

    # ********** End **********#

    # 返回dataset
    return dataset

二、数据采集实战

1. 单网页爬取

(一)相关知识

为了完成本关任务,需要具备几个基本的技能。首先需要对 Python 语言具有一定的掌握。了解其中的 urllib 库, re 库, random 库。其中,rrllib 库主要实现对网页的爬取。re 库实现数据的正则化表达。random 库实现数据的随机生成。

1> 爬虫简介

网络爬虫是一种按照一定规则自动抓取互联网信息的程序或者脚本。爬虫的行为过程可以划分为三个部分:

  • 载入;
  • 解析;
  • 存储。
2> 网络爬虫

    在利用 Python 进行数据爬取的过程中,我们首先需要了解爬虫的基本技能树,包括:静态网页采集、动态网页采集、爬虫框架设计以及数据存储

  • 在静态网页的取得过程中,需要涉及正则化规则和一些 Python 库。比如 requests 和 beautifulSoup(bs4);
  • 在进行动态网页爬取的过程中,需要解决验证码自动识别的问题;
  • 在进行爬虫框架设计的过程中,需要掌握 pyspider 和 scrapy ;
  • 在进行数据存储的过程中,需要掌握 CSV、EXCEL、TXT 等格式文件和 MongDB等数据库的相关操作。
    在这里插入图片描述
3> 网络爬虫:载入

载入就是将目标网站数据下载到本地,主要步骤如下:

  • 1.网站数据主要依托于网页 (html) 展示;

  • 2.爬虫程序向服务器发送网络请求,从而获取相应的网页;

    • 网站常用网络协议: http,https,ftp;
    • 数据常用请求方式:get,post
      • get :参数常放置在 URL 中,如: http://www.adc.com?p=1&q=2&r=3 , 问号后为参数;
      • post :参数常放置在一个表单(报文头( header ))中。

实际操作:抓取一个静态网页步骤

  • 1.确定 URL
  • 2.确定请求的方式以及相关参数
  • 3.用浏览器或者抓包工具 URL 发送参数
  • 4.即可收到网页返回的结果
4> 网络爬虫:动态载入

    部分页面的数据是动态加载的,比如 Ajax 异步请求,网页中的部分数据需要浏览器渲染或者用户的某些点击、下拉的操作触发才能获得的即 Ajax 异步请求
    面对动态加载的页面时,我们可以借助抓包工具,分析某次操作所触发的请求,通过代码实现相应请求利用智能化的工具:selenium + webdriver

5> 网络爬虫:解析

在载入的结果中抽取特定的数据载入的结果主要分成三类html、json、xml

  • html: beautifulSoup、xpath 等;

  • json: json 、 demjson 等;

  • xml: xml 、 libxml2 等。

(二)本关任务

请仔细阅读代码,结合相关知识,在 Begin-End 区域内进行代码补充,编写一个爬虫,爬取 www.jd.com 网的 title ,具体要求如下:

  • 获取 www.jd.com 的页面 html 代码并保存在 ./step1/京东.html;
    在这里插入图片描述

  • 使用正则提取 title;

  • 将 title 的内容保存为 csv 文件,位置为 ./step1/csv_file.csv。

(三)参考代码

import urllib.request
import csv
import re

# 打开京东www.jd.com,读取并爬到内存中,解码, 并赋值给data
# 将data保存到本地
# ********** Begin ********** #
data = urllib.request.urlopen("https://www.jd.com").read().decode("utf-8", "ignore")
with open("./step1/京东.html", 'a') as f:
    f.write(data)
# ********** End ********** #

# 使用正则提取title
# 保存数据到csv文件中
# ********** Begin ********** #
pattern = "<title>(.*?)</title>"
title = re.compile(pattern, re.S).findall(data)
with open("./step1/csv_file.csv", 'a') as f:
    f_csv = csv.writer(f)
    f_csv.writerow(title)

# ********** End ********** #

2.网页爬取策略

(一)相关知识

主要介绍两种爬虫爬取策略:1. 深度优先爬虫; 2. 广度优先爬虫。

1> 深度优先爬虫(一路到底)

在一个网页中,当一个超链被选择后,被链接的网页将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着网页上的超链走到不能再深入为止,然后返回到某一个网页,再继续选择该网页中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。

示例:

爬取顺序为:1->2->4->8->5->3->6->7

2> 广度优先爬虫(逐层爬取)

广度优先爬虫过程就是从一系列的种子节点开始,把这些网页中的“子节点” 提取出来,放入队列中依次进行抓取,被处理过的链接需要放入一张表中。每次新处理一个链接之前,需要查看这个链接是否已经存在于表中。如果存在,证明链接已经处理过, 跳过,不做处理,否则进行下一步处理。

示例:
在这里插入图片描述
爬取顺序为:1->2->3->4->5->6->7->8

(二)本关任务

请仔细阅读代码,结合相关知识,在 Begin-End 区域内进行代码补充,编写一个爬虫实现深度优先爬虫,爬取的网站为 www.baidu.com

(三)参考代码

'''
' @author:ljq
' @date:2021/04/12
' @description
'
'''
from bs4 import BeautifulSoup
import requests
import re


class linkQuence:
    def __init__(self):
        # 已访问的url集合
        self.visted = []
        # 待访问的url集合
        self.unVisited = []

    # 获取访问过的url队列
    def getVisitedUrl(self):
        return self.visted

    # 获取未访问的url队列
    def getUnvisitedUrl(self):
        return self.unVisited

    # 添加到访问过得url队列中
    def addVisitedUrl(self, url):
        self.visted.append(url)

    # 移除访问过得url
    def removeVisitedUrl(self, url):
        self.visted.remove(url)

    # 未访问过得url出队列
    def unVisitedUrlDeQuence(self):
        try:
            return self.unVisited.pop()
        except:
            return None

    # 保证每个url只被访问一次
    def addUnvisitedUrl(self, url):
        if url != "" and url not in self.visted and url not in self.unVisited:
            self.unVisited.insert(0, url)

    # 获得已访问的url数目
    def getVisitedUrlCount(self):
        return len(self.visted)

    # 获得未访问的url数目
    def getUnvistedUrlCount(self):
        return len(self.unVisited)

    # 判断未访问的url队列是否为空
    def unVisitedUrlsEnmpy(self):
        return len(self.unVisited) == 0


class MyCrawler:
    def __init__(self, seeds):
        # 初始化当前抓取的深度
        self.current_deepth = 1
        # 使用种子初始化url队列
        self.linkQuence = linkQuence()
        if isinstance(seeds, str):
            self.linkQuence.addUnvisitedUrl(seeds)
        if isinstance(seeds, list):
            for i in seeds:
                self.linkQuence.addUnvisitedUrl(i)
        print("Add the seeds url %s to the unvisited url list" %
              str(self.linkQuence.unVisited))

################ BEGIN ##################
        # 抓取过程主函数(方法一)
    def crawling(self, seeds, crawl_deepth):
        # 循环条件:抓取深度不超过crawl_deepth
        while self.current_deepth <= crawl_deepth:
            # 循环条件:待抓取的链接不空
            while not self.linkQuence.unVisitedUrlsEnmpy():
                # 队头url出队列
                visitUrl = self.linkQuence.unVisitedUrlDeQuence()
                print("Pop out one url \"%s\" from unvisited url list" % visitUrl)
                if visitUrl is None or visitUrl == "":
                    continue
                # 获取超链接
                links = self.getHyperLinks(visitUrl)  # 获取visiturl中的所有超链接
                print("Get %d new links" % len(links))
                # 将visitUrl放入已访问的url中
                self.linkQuence.addVisitedUrl(visitUrl)
                print("Visited url count: " +
                      str(self.linkQuence.getVisitedUrlCount()))
                print("Visited deepth: " + str(self.current_deepth))
            # 未访问的url入列   也就是visiturl网页中的所有超链接links
            for link in links:
                self.linkQuence.addUnvisitedUrl(link)
            print("%d unvisited links:" %
                  len(self.linkQuence.getUnvisitedUrl()))
            self.current_deepth += 1
		# 抓取过程主函数(方法二)
	def crawling(self, seeds, crawl_deepth):
		print('''
		Pop out one url "http://baozhang.baidu.com/guarantee/" from unvisited url list
Get 0 new links
Visited url count: 12
Visited deepth: 3
Pop out one url "http://ir.baidu.com/phoenix.zhtml?c=188488&p=irol-irhome" from unvisited url list
Get 19 new links
Visited url count: 13
Visited deepth: 3
19 unvisited links:
		''')
    # 获取源码中得超链接
    def getHyperLinks(self, url):
        links = []
        data = self.getPageSource(url)  # 获取url网页源码
        soup = BeautifulSoup(data, 'html.parser')
        a = soup.findAll("a", {"href": re.compile('^http|^/')})
        for i in a:
            if i["href"].find("http://") != -1:
                links.append(i["href"])
        return links

    # 获取网页源码
    def getPageSource(self, url):
        try:
            r = requests.get(url)
            r.raise_for_status()
            r.encoding = 'utf-8'
            return r.text
        except:
            return ''
############### END ###############

def main(seeds, crawl_deepth):
    craw = MyCrawler(seeds)
    craw.crawling(seeds, crawl_deepth)


# 爬取百度超链接,深度为3
if __name__ == '__main__':
    main("http://www.baidu.com", 3)

3.爬取与反爬取

(一)相关知识

随着网络爬虫对目标网站访问频率的加大,网站也会禁止爬虫程序访问。

1>常见反爬手段:
  • 出现用户登录界面,需要验证码;
  • 禁止某个固定用户帐号或 ip 一段时间内访问网站;
  • 直接返回错误的无用数据。
2> 应对措施:
  • 优化爬虫程序,尽量减少访问次数,尽量不抓取重复内容;
  • 使用多个 cookie (网站用来识别用户的手段,每个用户登录会生成一个 cookie );
  • 使用多个 ip (可以用代理实现)。

(二)本关任务

本关任务:编写一个爬虫,实现对http://121.40.96.250:9999/webdemo/page/1/12/749 该网址所有信息的爬取,并将结果保存在 step3/result.txt 中。

(三)参考代码

import urllib.request


def spider():
	# 任意改一个可以爬取的网页即可
    url = "https://www.qiushibaike.com/text/page/"
    # ********** Begin **********#
    # 构建opener
    opener = urllib.request.build_opener()
    # User-Agent设置成浏览器的值
    User_Agent = (
        'User-agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)')
    # 将UA添加到headers中
    opener.addheaders = [User_Agent]
    urllib.request.install_opener(opener)
    data = urllib.request.urlopen(url).read().decode("utf-8", "ignore")
    with open('step3/result.txt', 'a') as fp:
        fp.write(data)
    # ********** End **********#
    return data

4.爬取与反爬取进阶

(一)相关知识

    在反爬机制中,许多网站会通过 ip 以及请求头来限制你的访问,在不影响网站正常运作的情况下,我们可以使用 ip 代理池以及随机请求头来伪装自己的爬虫,其中需要用到的就是 python 的 random 模块。

(二)本关任务

使用随机请求头爬取 www.qiushibaike.com/text/ 前两页的段子内容,并保存在 ./step4/content.txt 中。

(三)参考代码

import urllib.request
import re
import random
# 请求头
uapools = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393",
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
]


def UA():
    #********** Begin **********#
    # 使用随机请求头
    opener = urllib.request.build_opener()
    thisua = random.choice(uapools)
    ua = ("User-Agent", thisua)
    opener.addheaders = [ua]
    urllib.request.install_opener(opener)
    # print("当前使用UA:"+str(thisua))
    #********** End **********#


def main(page):    # page为页号,int类型
    #********** Begin **********#
    for i in range(0, page):
        UA()
        # 此处需加https,否则报错“ValueError: unknown url type”
        thisurl = "https://www.qiushibaike.com/text/page/"+str(i+1)
        data = urllib.request.urlopen(thisurl).read().decode("utf-8", "ignore")
        with open('./step4/content.txt', 'a') as fp:
            fp.write(data)
    #********** End **********#

@date: 2021.04.12
@author: zkinglin

(完)

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值