爬虫系列(数据标准化)

上一期我们介绍了使用 Python 数据清洗的相关方法,本篇文章我们介绍数据标准化的相关方法。
每个人都会遇到一些样式设计不够人性化的网页,比如“请输入你的电话号码,号码格式为 xxx-xxxx-xxxx”。
作为一名优秀的程序员,你可能会问:”为什么不自动对输入的数据进行清洗,去掉非数字内容,然后自动把数据加上分隔符呢?“数据标准化过程要确保清洗后的数据在语言学上是等价的,比如电话号码虽然显示成”134-1234-5678“和”134-12345678“两种形式,但是实际号码是一样的。
还是用上一期的 n-gram 示例,让我们在上面增加一些数据标准化的特征。
上期文章内容的明显问题,就是输出结果中包含太多重复的 2-gram 序列。程序把每个 2-gram 都加入了列表,没有统计过序列的频率。掌握 2-gram 序列的频率,而不只是知道某个序列是否存在,这有助于对比不同的数据清洗和数据标准化算法的效果。如果数据标准化成功了,那么唯一的 n-gram 序列数量就会减少,而 n-gram 序列的总数(任何一个 n-gram 序列和与之重复的序列都被看成一个 n-gram 序列)不变,也就是说,同样数量的 n-gram 序列,经过去重之后“容量(bucket)”会减少。
不过 Python 的字典是无序的,不能像数组一样直接对 n-gram 序列频率进行排序。字典内部元素的位置是不固定的,排序之后再次使用时还是发生变化,除非你把排序过的字典里的值复制到其他类型中进行排序。在 Python 中 collections 库里面有一个 OrderedDict 可以解决这个问题:

import re import string from collections import OrderedDictfrom utils import connection_utilclass DataCleaning(object): def __init__(self): self._target_url = 'https://en.wikipedia.org/wiki/python_(programming_language)' self._init_connection = connection_util.ProcessConnection()def getNgrams(self, input, n): input = self.clean_input(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@staticmethod def clean_input(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") input = input.split(' ') clean_input = [] for item in input: # string.punctuation 获取所有的标点符号 item = item.strip(string.punctuation) if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'): clean_input.append(item) return clean_inputdef get_result(self): # 连接目标网站,获取内容 get_content = self._init_connection.init_connection(self._target_url) if get_content: content = get_content.find("div", {"id": "mw-content-text"}).get_text() ngrams = self.getNgrams(content, 2) ngrams = OrderedDict(sorted(ngrams.items(), key=lambda t: t[1], reverse=True)) print(ngrams) print("2-grams count is: " + str(len(ngrams)))if __name__ == '__main__': DataCleaning().get_result()

这我们使用了 Python 的排序函数(https://docs.python.org/zh-cn/3/howto/sorting.html)把序列频率转换成 OrderedDict 对象,并按照频率值排序。结果如下:
('Python Software', 37), ('2021 Retrieved', 36), ('Foundation Archived', 35), ('on June', 34), ('of Python', 28), ('in the', 25), ('such as', 23)

去掉语气词,以及连接词之后频率最高的是“Software Foundation”和“Python Software”。但是仔细观察结果会发现会有大小写字母的影响,“Python Software”有三次是“Python software”的形式,同样,“Van Rossum”和“van Rossum”也是作为两个序列来统计的。
因此,我们增加一行代码:
input = input.upper()

clean_input() 函数里,这样就解决了上面的问题,同时减少了重复的 2-gram 序列。
除了这些,还需要在考虑一下,自己计划为数据标准化的进一步深入再投入多少计算力。很多单词在不同的环境里会使用不同的拼写形式,其实都是等价的,但是为了解决这种等价关系,你需要对每个单词进行检查,判断是否和其他单词有等价关系。
比如,“Python 1st”和“Python first”都出现在 2-gram 序列里面。但是,如果增加一条规则:“让所有‘first’、‘secode’、‘third’……与 1st、2nd、3rd……等价”,那么每个单词就需要额外增加十几次检查。
同理,连字符使用不一致(像“co-ordinated”和“coordinated”)、单词拼写错误以及其他语病(incongruities),都可能对 n-gram 序列的分组结果造成影响,如果语病很严重的话,很可能彻底打乱输出结果。
对连字符单词的一个处理方法是,首先把连字符去掉,然后把单词当作一个字符串,这可能需要在程序中增加一步操作。但是,这样做也可能把带连字符的短语(这种很常见,比如:“just-in-time”、“object-oriented”等)处理成一个字符串。要是换一种做法,把连字符换成空格可能会更好一些。但是就得准备见到“co ordinated”和“ordinated attack”之类的 2-gram 序列了!
总结 这篇文章主要讲解了在英文中关于数据标准化的相关内容,首先是对单词出现的频率进行排序,之后对一些大小写进行转换,缩小 2-gram 序列的重复内容,之后对一些连字符以及一些语法上的习惯进行处理。
处理完成后的内容我们可以制作一个词云,如下:
爬虫系列(数据标准化)
文章图片

以上就是这篇文章的全部内容。
源代码已经托管于 Github 当中,地址:https://github.com/sycct/Scra...
【爬虫系列(数据标准化)】如果有任何问题,欢迎大家 issue。

    推荐阅读