数据清洗
到目前为止,我们还没有处理过那些样式不规范的数据,要么是使用样式规范的数据源, 要么就是彻底放弃样式不符合我们预期的数据。但是在网络数据采集中,你通常无法对采 集的数据样式太挑剔。
由于错误的标点符号、大小写字母不一致、断行和拼写错误等问题,零乱的数据(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,不仅可以快速简单地清理数据,还可以让非 编程人员轻松地看见和使用你的数据。
- 安装
OpenRefine 的独特之处在于虽然它的界面是一个浏览器,但实际上是一个桌面应用,必 须下载并安装。你可以从它的下载页面(http://openrefine.org/download.html)下载对应 Linux、Windows 和 Mac OS X 系统的版本。
- 使用OpenRefine
在下面的例子中,我们将使用维基百科的“文本编辑器对比”表格(https://en.wikipedia. org/wiki/Comparison_of_text_editors)里的内容,如图 7-1 所示。虽然这个表的样式比较规 范,但里面包含了多次编辑的痕迹,所以还是有一些样式不一致的地方。另外,因为这个 数据是写给人看的,而不是让机器看的,所以原来使用的一些样式(比如用“Free”而不 是“$0.00”)不太合适作为 OpenRefine 程序的输入数据。
标签:item,import,content,采集,gram,output,input,清洗,数据 From: https://blog.51cto.com/hiszm/6186272