Redis与数据解析实战指南
1. Redis数据存储
Redis是一个高性能的键值对存储数据库,在数据处理和存储方面有着广泛的应用。下面将详细介绍Redis在不同数据结构存储上的应用。
1.1 RedisHashMap
RedisHashMap可作为持久化的键值字典单独使用。虽然它能支持大量的键和任意字符串值,但对于整数值和较少数量的键,其存储结构更为优化。尽管Redis作为网络数据库速度很快,但相较于内存中的FreqDist,它的速度会明显变慢。不过,使用Redis可以获得数据的持久性和并发处理能力。
1.2 存储条件频率分布
nltk.probability.ConditionalFreqDist
类是
FreqDist
实例的容器,每个条件对应一个
FreqDist
,用于统计依赖于其他条件的频率。我们可以基于Redis创建一个与之API兼容的类
RedisConditionalHashFreqDist
。
准备工作 :需要安装Redis和redis - py,并启动redis - server实例。
操作步骤
:
在
redisprob.py
中定义
RedisConditionalHashFreqDist
类,该类继承自
nltk.probability.ConditionalFreqDist
并覆盖
__getitem__()
方法:
from nltk.probability import ConditionalFreqDist
from rediscollections import encode_key
class RedisConditionalHashFreqDist(ConditionalFreqDist):
def __init__(self, r, name, cond_samples=None):
self._r = r
self._name = name
ConditionalFreqDist.__init__(self, cond_samples)
for key in self._r.keys(encode_key('%s:*' % name)):
condition = key.split(':')[1]
self[condition] # calls self.__getitem__(condition)
def __getitem__(self, condition):
if condition not in self._fdists:
key = '%s:%s' % (self._name, condition)
val = RedisHashFreqDist(self._r, key)
super(RedisConditionalHashFreqDist, self).__setitem__(condition, val)
return super(RedisConditionalHashFreqDist, self).__getitem__(condition)
def clear(self):
for fdist in self.values():
fdist.clear()
创建该类的实例:
from redis import Redis
from redisprob import RedisConditionalHashFreqDist
r = Redis()
rchfd = RedisConditionalHashFreqDist(r, 'condhash')
print(rchfd.N()) # 输出: 0
print(rchfd.conditions()) # 输出: []
rchfd['cond1']['foo'] += 1
print(rchfd.N()) # 输出: 1
print(rchfd['cond1']['foo']) # 输出: 1
print(rchfd.conditions()) # 输出: ['cond1']
rchfd.clear()
工作原理
:
RedisConditionalHashFreqDist
使用名称前缀来引用
RedisHashFreqDist
实例。传入的名称作为基名,与每个条件组合,为每个
RedisHashFreqDist
创建唯一名称。例如,基名为
condhash
,条件为
cond1
,则
RedisHashFreqDist
的最终名称为
condhash:cond1
。
1.3 存储有序字典
Redis支持有序字典,其键为字符串,值为浮点分数。这种结构在计算信息增益和存储单词及其分数时非常有用。
准备工作 :安装Redis和redis - py,并启动redis - server实例。
操作步骤
:
在
rediscollections.py
中定义
RedisOrderedDict
类,该类继承自
collections.MutableMapping
:
import collections
from rediscollections import encode_key
class RedisOrderedDict(collections.MutableMapping):
def __init__(self, r, name):
self._r = r
self._name = encode_key(name)
def __iter__(self):
return iter(self.items())
def __len__(self):
return self._r.zcard(self._name)
def __getitem__(self, key):
return self._r.zscore(self._name, encode_key(key))
def __setitem__(self, key, score):
self._r.zadd(self._name, encode_key(key), score)
def __delitem__(self, key):
self._r.zrem(self._name, encode_key(key))
def keys(self, start=0, end=-1):
return self._r.zrevrange(self._name, start, end)
def values(self, start=0, end=-1):
return [v for (k, v) in self.items(start=start, end=end)]
def items(self, start=0, end=-1):
return self._r.zrevrange(self._name, start, end, withscores=True)
def get(self, key, default=0):
return self[key] or default
def iteritems(self):
return iter(self)
def clear(self):
self._r.delete(self._name)
创建该类的实例:
from redis import Redis
from rediscollections import RedisOrderedDict
r = Redis()
rod = RedisOrderedDict(r, 'test')
print(rod.get('bar')) # 输出: None
print(len(rod)) # 输出: 0
rod['bar'] = 5.2
print(rod['bar']) # 输出: 5.2000000000000002
print(len(rod)) # 输出: 1
print(rod.items()) # 输出: [(b'bar', 5.2)]
rod.clear()
工作原理
:
| 方法 | 功能 | Redis命令 |
| ---- | ---- | ---- |
|
__len__()
| 获取有序集合中的元素数量 |
zcard
|
|
__getitem__()
| 获取键的分数,键不存在时返回0 |
zscore
|
|
__setitem__()
| 向有序集合中添加键及其分数,键已存在则更新分数 |
zadd
|
|
__delitem__()
| 从有序集合中删除键 |
zrem
|
|
keys()
| 获取有序集合中按最高分数排序的所有键 |
zrevrange
|
|
values()
| 从
items()
方法中提取所有分数 | - |
|
items()
| 获取有序集合中按最高分数排序的键值对列表 |
zrevrange
|
|
clear()
| 从Redis中删除整个有序集合 |
delete
|
默认情况下,Redis有序集合中的项按从低到高排序,而我们通常希望按从高到低排序,因此
keys()
和
items()
方法使用
zrevrange
而不是
zrange
。
1.4 分布式单词评分
我们可以结合Redis和execnet进行分布式单词评分。
准备工作 :安装Redis、redis - py和execnet,并在本地主机上启动redis - server实例。
操作步骤 :
from dist_featx import score_words
from nltk.corpus import movie_reviews
labels = movie_reviews.categories()
labelled_words = [(l, movie_reviews.words(categories=[l])) for l in labels]
word_scores = score_words(labelled_words)
print(len(word_scores)) # 输出: 39767
topn_words = word_scores.keys(end=1000)
print(topn_words[0:5]) # 输出: [b'bad', b',', b'and', b'?', b'movie']
from redis import Redis
r = Redis()
[r.delete(key) for key in ['word_fd', 'label_word_fd:neg', 'label_word_fd:pos', 'word_scores']]
工作原理
:
dist_featx.py
模块中的
score_words()
函数完成以下操作:
1. 打开网关和通道,向每个通道发送初始化数据。
2. 通过通道发送每个
(label, words)
元组进行计数。
3. 向每个通道发送完成消息,等待完成回复,然后关闭通道和网关。
4. 根据计数计算每个单词的分数,并存储在
RedisOrderedDict
中。
import itertools, execnet, remote_word_count
from nltk.metrics import BigramAssocMeasures
from redis import Redis
from redisprob import RedisHashFreqDist, RedisConditionalHashFreqDist
from rediscollections import RedisOrderedDict
def score_words(labelled_words, score_fn=BigramAssocMeasures.chi_sq, host='localhost', specs=[('popen', 2)]):
gateways = []
channels = []
for spec, count in specs:
for i in range(count):
gw = execnet.makegateway(spec)
gateways.append(gw)
channel = gw.remote_exec(remote_word_count)
channel.send((host, 'word_fd', 'label_word_fd'))
channels.append(channel)
cyc = itertools.cycle(channels)
for label, words in labelled_words:
channel = next(cyc)
channel.send((label, list(words)))
for channel in channels:
channel.send('done')
assert 'done' == channel.receive()
channel.waitclose(5)
for gateway in gateways:
gateway.exit()
r = Redis(host)
fd = RedisHashFreqDist(r, 'word_fd')
cfd = RedisConditionalHashFreqDist(r, 'label_word_fd')
word_scores = RedisOrderedDict(r, 'word_scores')
n_xx = cfd.N()
for label in cfd.conditions():
n_xi = cfd[label].N()
for word, n_ii in cfd[label].iteritems():
word = word.decode()
n_ix = fd[word]
if n_ii and n_ix and n_xi and n_xx:
score = score_fn(n_ii, (n_ix, n_xi), n_xx)
word_scores[word] = score
return word_scores
remote_word_count.py
模块:
from redis import Redis
from redisprob import RedisHashFreqDist, RedisConditionalHashFreqDist
if __name__ == '__channelexec__':
host, fd_name, cfd_name = channel.receive()
r = Redis(host)
fd = RedisHashFreqDist(r, fd_name)
cfd = RedisConditionalHashFreqDist(r, cfd_name)
for data in channel:
if data == 'done':
channel.send('done')
break
label, words = data
for word in words:
fd[word] += 1
cfd[label][word] += 1
2. 特定数据类型解析
在实际的数据处理中,我们经常需要解析特定类型的数据,如日期、时间和HTML等。下面将介绍使用不同的库来完成这些任务。
2.1 使用dateutil解析日期和时间
dateutil是Python中解析日期和时间的优秀库,其
parser
模块可以解析多种格式的日期时间字符串,
tz
模块可用于时区查找和转换。
准备工作 :使用pip或easy_install安装dateutil 2.0版本,以确保Python 3兼容性。安装命令如下:
sudo pip install dateutil==2.0
或
sudo easy_install dateutil==2.0
完整文档可参考:http://labix.org/python - dateutil
操作步骤 :
from dateutil import parser
# 解析不同格式的日期时间字符串
print(parser.parse('Thu Sep 25 10:36:28 2010'))
print(parser.parse('Thursday, 25. September 2010 10:36AM'))
print(parser.parse('9/25/2010 10:36:28'))
print(parser.parse('9/25/2010'))
print(parser.parse('2010-09-25T10:36:28Z'))
如果无法解析字符串,
parse()
函数将引发
ValueError
异常。
工作原理
:
dateutil的解析器不使用正则表达式,而是通过识别可识别的标记并猜测其含义来解析日期时间字符串。日期格式的顺序很重要,不同文化可能使用不同的日期格式,如Month/Day/Year或Day/Month/Year。
parse()
函数提供了
dayfirst
和
yearfirst
两个可选关键字参数来处理这些问题。
# 处理Day/Month/Year格式
print(parser.parse('25/9/2010', dayfirst=True))
# 处理两位数年份的歧义
print(parser.parse('10-9-25'))
print(parser.parse('10-9-25', yearfirst=True))
此外,dateutil的解析器还支持模糊解析,允许忽略日期时间字符串中的无关字符。
try:
print(parser.parse('9/25/2010 at about 10:36AM'))
except ValueError:
print('cannot parse')
print(parser.parse('9/25/2010 at about 10:36AM', fuzzy=True))
通过以上介绍,我们可以看到Redis在数据存储和分布式处理方面的强大功能,以及dateutil在日期时间解析上的灵活性。这些工具和技术可以帮助我们更高效地处理和分析各种类型的数据。
Redis与数据解析实战指南
2.2 时区查找和转换
在使用
dateutil
完成日期和时间解析后,我们可能还需要进行时区的查找和转换,这就需要用到
dateutil
的
tz
模块。
准备工作
:确保已经安装了
dateutil 2.0
版本,安装方式如前文所述。
操作步骤 :
from dateutil import parser
from dateutil import tz
# 解析一个带有时区信息的日期时间字符串
dt = parser.parse('2024-01-01T12:00:00+08:00')
# 定义目标时区
target_tz = tz.gettz('America/New_York')
# 进行时区转换
dt_converted = dt.astimezone(target_tz)
print(f"原始时间: {dt}")
print(f"转换后的时间: {dt_converted}")
工作原理
:
dateutil
的
tz
模块提供了
gettz
函数,用于获取不同的时区对象。
astimezone
方法则可以将一个
datetime
对象从当前时区转换到指定的时区。在进行时区转换时,
astimezone
会根据两个时区之间的时差自动调整时间。
2.3 从HTML中提取URL
在处理网页数据时,我们常常需要从HTML中提取出其中包含的URL。
lxml
是一个强大的Python库,可以帮助我们完成这个任务。
准备工作
:安装
lxml
库,可以使用以下命令:
pip install lxml
操作步骤 :
from lxml import html
import requests
# 发送请求获取网页内容
url = 'https://example.com'
response = requests.get(url)
tree = html.fromstring(response.content)
# 提取所有的URL
urls = tree.xpath('//a/@href')
for url in urls:
print(url)
工作原理
:
lxml
的
html
模块提供了
fromstring
函数,用于将HTML内容解析为一个可操作的树结构。
xpath
方法则可以根据XPath表达式来选择树中的元素。在上述代码中,
//a/@href
表示选择所有
<a>
标签的
href
属性,从而提取出所有的URL。
2.4 清理和剥离HTML
有时候,我们只需要HTML中的文本内容,而不需要其中的HTML标签。
lxml
同样可以帮助我们完成HTML的清理和剥离工作。
准备工作
:确保已经安装了
lxml
库。
操作步骤 :
from lxml import html
html_content = '<p>这是一段 <b>HTML</b> 内容。</p>'
tree = html.fromstring(html_content)
# 剥离HTML标签,获取纯文本
text = tree.text_content()
print(text)
工作原理
:
lxml
的
text_content
方法可以递归地遍历树结构,提取出其中的所有文本内容,并去除HTML标签。
2.5 使用BeautifulSoup转换HTML实体
HTML实体是一种用于表示特殊字符的编码方式,如
<
表示
<
。
BeautifulSoup
是一个用于解析HTML和XML文档的Python库,可以帮助我们将HTML实体转换为对应的字符。
准备工作
:安装
BeautifulSoup
库,可以使用以下命令:
pip install beautifulsoup4
操作步骤 :
from bs4 import BeautifulSoup
html_content = '这是一个 < 符号。'
soup = BeautifulSoup(html_content, 'html.parser')
# 转换HTML实体
text = soup.get_text()
print(text)
工作原理
:
BeautifulSoup
在解析HTML内容时,会自动将HTML实体转换为对应的字符。
get_text
方法则用于提取解析后的文本内容。
2.6 检测和转换字符编码
在处理不同来源的文本数据时,我们可能会遇到字符编码不一致的问题。
charade
和
UnicodeDammit
可以帮助我们检测和转换字符编码。
准备工作
:安装
chardet
库(
charade
的替代品),可以使用以下命令:
pip install chardet
操作步骤 :
import chardet
# 模拟一个未知编码的文本
unknown_encoded_text = b'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82'
# 检测编码
result = chardet.detect(unknown_encoded_text)
encoding = result['encoding']
# 解码文本
decoded_text = unknown_encoded_text.decode(encoding)
print(f"检测到的编码: {encoding}")
print(f"解码后的文本: {decoded_text}")
工作原理
:
chardet
库通过分析文本的字节模式来猜测其编码。
detect
方法返回一个包含编码信息和置信度的字典。我们可以根据这个字典中的编码信息来对文本进行解码。
总结
通过本文的介绍,我们详细了解了Redis在不同数据结构存储上的应用,包括
RedisHashMap
、
RedisConditionalHashFreqDist
和
RedisOrderedDict
,以及如何结合
execnet
进行分布式单词评分。同时,我们还学习了使用
dateutil
解析日期和时间、进行时区转换,使用
lxml
从HTML中提取URL、清理和剥离HTML,使用
BeautifulSoup
转换HTML实体,以及使用
chardet
检测和转换字符编码。这些工具和技术可以帮助我们更高效地处理和分析各种类型的数据,为实际的数据处理工作提供了有力的支持。
下面是一个简单的mermaid流程图,展示了从HTML数据处理到最终文本提取的流程:
graph LR
A[获取HTML内容] --> B[使用lxml解析HTML]
B --> C{选择操作}
C -->|提取URL| D[使用xpath提取URL]
C -->|清理HTML| E[使用text_content清理标签]
E --> F[使用BeautifulSoup转换HTML实体]
F --> G[使用chardet检测和转换编码]
D --> H[输出URL列表]
G --> I[输出纯文本]
通过这个流程图,我们可以更清晰地看到整个数据处理的流程,以及各个步骤之间的关系。
超级会员免费看
1289

被折叠的 条评论
为什么被折叠?



