python数据采集7-数据清洗

python数据采集7-数据清洗

数据清洗

到目前为止,我们还没有处理过那些样式不规范的数据,要么是使用样式规范的数据源,
要么就是彻底放弃样式不符合我们预期的数据。但是在网络数据采集中,你通常无法对采
集的数据样式太挑剔。

由于错误的标点符号、大小写字母不一致、断行和拼写错误等问题,零乱的数据(dirty
data)是网络中的大问题。本章将介绍一些工具和技术,通过改变代码的编写方式,帮你
从源头控制数据零乱的问题,并且对已经进入数据库的数据进行清洗。

编写代码清洗数据

和写代码处理异常一样,你也应该学习编写预防型代码来处理意外情况。

在语言学里有一个模型叫 n-gram,表示文字或语言中的 n 个连续的单词组成的序列。在进
行自然语言分析时,使用 n-gram 或者寻找常用词组,可以很容易地把一句话分解成若干个
文字片段。

这一节我们将重点介绍如何获取格式合理的 n-gram,并不用它们做任何分析。


from urllib.request import urlopen
from bs4 import BeautifulSoup

def getNgrams(input, n):
  input = input.split(' ')
  output = []
  for i in range(len(input)-n+1):
    output.append(input[i:i+n])
  return output

html = urlopen("https://baike.baidu.com/item/Python/407313")
bsObj = BeautifulSoup(html, "html.parser")
content = bsObj.find("div", {"class":"para"}).get_text()
ngrams = getNgrams(content, 2)
print(ngrams)
print("2-grams count is: "+str(len(ngrams)))


ngrams 函数把一个待处理的字符串分成单词序列(假设所有单词按照空格分开),然后增
加到 n-gram 模型(本例中是 2-gram)里形成以每个单词开始的二元数组。


 ['官方文档(英文)\xa0\n.Python3', '官方文档[引用日期2015-01-14]\n10.\n\xa0\xa0\n网络课程'], ['官方文档[引用日期2015-01-14]\n10.\n\xa0\xa0\n网络课程', 'python'], ['python', '网络教育-百度传课\xa0\n.百度传课[引用日期2016-09-24]\n\n\n']]
 

不过,同时也会出现一些零乱的数据:


\n#!/usr/bin/python\r\nimport\xa0os\r\nprint"Content-type:text/html\\r\\n\\r\\n"\r\nprint"Environment"\r\nfor\xa0param\xa0in\xa0os.environ.keys():\r\n\xa0\xa0\xa0\xa0print"<b>%20s</b>:%s<\\br>"\xa0%(param,os.environ[param])\n\n\n\n\nPython特点\n编辑\n\n\n\n\n\n\n\nPython优点\n\n简单:Python是一种代表简单主义思想的语言。阅读一个良好的Python程序就感觉像是在读英语一样。它使你能够专注于解决问题而不是去搞明白语言本身。\n易学:Python极其容易上手,因为Python有极其简单的说明文档[6]\xa0\n。\n速度快:Python'], ['服务器的主机名、别名或IP地址。\nSERVER_SOFTWARE\n这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(Unix)\n以下是一个简单的CGI脚本输出


让我们首先用一些正则表达式来移除转义字符( \n ),再把 Unicode 字符过滤掉。我们可以
通过下面的函数对之前输出的结果进行清理:

这里首先把内容中的换行符(或者多个换行符)替换成空格,然后把连续的多个空格替换
成一个空格,确保所有单词之间只有一个空格。最后,把内容转换成 UTF-8 格式以消除转
义字符。


def ngrams(input, n):
    content = re.sub('\n+', " ", content)
    content = re.sub(' +', " ", content)
    content = bytes(content, "UTF-8")
    content = content.decode("ascii", "ignore")
    print(content)
    input = input.split(' ')
    output = []
    for i in range(len(input)-n+1):
         output.append(input[i:i+n])
    return output

  • 剔除单字符的“单词”,除非这个字符是“i”或“a”;
  • 剔除维基百科的引用标记(方括号包裹的数字,如 [1]);
  • 剔除标点符号(注意:这个规则有点儿矫枉过正,在第 9 章我们将详细介绍,本例暂时
    这样处理)。

from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
import string

def cleanInput(input):
    input = re.sub('\n+', " ", input)
    input = re.sub('\[[0-9]*\]', "", input)
    input = re.sub(' +', " ", input)
    input = bytes(input, "UTF-8")
    input = input.decode("ascii", "ignore")
    cleanInput = []
    input = input.split(' ')
    for item in input:
        item = item.strip(string.punctuation)
        if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'):
    cleanInput.append(item)
    return cleanInput
def ngrams(input, n):
    input = cleanInput(input)
    output = []
    for i in range(len(input)-n+1):
          output.append(input[i:i+n])
    return output



from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
import string
from collections import OrderedDict

def cleanInput(input):
    input = re.sub('\n+', " ", input)
    input = re.sub('\[[0-9]*\]', "", input)
    input = re.sub(' +', " ", input)
    input = bytes(input, "UTF-8")
    input = input.decode("ascii", "ignore")
    cleanInput = []
    input = input.split(' ')
    for item in input:
        item = item.strip(string.punctuation)
        if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'):
            cleanInput.append(item)
    return cleanInput

def getNgrams(input, n):
    input = cleanInput(input)
    output = dict()
    for i in range(len(input)-n+1):
        newNGram = " ".join(input[i:i+n])
        if newNGram in output:
            output[newNGram] += 1
        else:
            output[newNGram] = 1
    return output

html = urlopen("https://baike.baidu.com/item/Python/407313")
bsObj = BeautifulSoup(html, "html.parser")
content = bsObj.find("div", {"class":"main-content"}).get_text()
ngrams = getNgrams(content, 2)
ngrams = OrderedDict(sorted(ngrams.items(), key=lambda t: t[1], reverse=True))
print(ngrams)


这里用 import string 和 string.punctuation 来获取 Python 所有的标点符号。你可以在
Python 命令行看看标点符号有哪些:



>>> import string
>>> print(string.punctuation)
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

在循环体中用 item.strip(string.punctuation) 对内容中的所有单词进行清洗,单词两端
的任何标点符号都会被去掉,但带连字符的单词(连字符在单词内部)仍然会保留。

数据标准化

每个人都会遇到一些样式设计不够人性化的网页,比如“请输入你的电话号码。号码格式
必须是 xxx-xxx-xxxx”。

作为一名优秀的程序员,你可能会问:“为什么不自动地对输入的信息进行清洗,去掉非
数字内容,然后自动把数据加上分隔符呢?”数据标准化过程要确保清洗后的数据在语言
学或逻辑上是等价的,比如电话号码虽然显示成“(555) 123-4567”和“555.123.4567”两
种形式,但是实际号码是一样的。

还用之前的 n-gram 示例,让我们在上面增加一些数据标准化特征。

这段代码有一个明显的问题,就是输出结果中包含太多重复的 2-gram 序列。程序把每个
2-gram 序列都加入了列表,没有统计过序列的频率。掌握 2-gram 序列的频率,而不只是
知道某个序列是否存在,这不仅很有意思,而且有助于对比不同的数据清洗和数据标准化
算法的效果。如果数据标准化成功了,那么唯一的 n-gram 序列数量就会减少,而 n-gram
序列的总数(任何一个 n-gram 序列和与之重复的序列被看成一个 n-gram 序列)不变。也
就是说,同样数量的 n-gram 序列,经过去重之后“容量”(bucket)会减少。

数据存储后再清洗

对于编写代码清洗数据,你能做或想做的事情只有这些。除此之外,你可能还需要处理一
些别人创建的数据库,或者要对一个之前没接触过的数据库进行清洗。
很多程序员遇到这种情况的自然反应就是“写个脚本”,当然这也是一个很好的解决方法。

但是,还有一些第三方工具,像 OpenRefine,不仅可以快速简单地清理数据,还可以让非
编程人员轻松地看见和使用你的数据。

  1. 安装

OpenRefine 的独特之处在于虽然它的界面是一个浏览器,但实际上是一个桌面应用,必
须下载并安装。你可以从它的下载页面(http://openrefine.org/download.html)下载对应
Linux、Windows 和 Mac OS X 系统的版本。

  1. 使用OpenRefine

在下面的例子中,我们将使用维基百科的“文本编辑器对比”表格(https://en.wikipedia.
org/wiki/Comparison_of_text_editors)里的内容,如图 7-1 所示。虽然这个表的样式比较规
范,但里面包含了多次编辑的痕迹,所以还是有一些样式不一致的地方。另外,因为这个
数据是写给人看的,而不是让机器看的,所以原来使用的一些样式(比如用“Free”而不
是“$0.00”)不太合适作为 OpenRefine 程序的输入数据。

posted @ 2018-12-23 22:24  孙中明  阅读(370)  评论(0编辑  收藏  举报