Redis与特定数据类型解析的综合应用
一、Redis数据存储结构
Redis作为一种高性能的网络数据库,提供了多种数据存储结构,这些结构在不同的场景下各有优势。
1. RedisHashMap
RedisHashMap可单独用作持久化的键值字典。虽然它能支持大量的键和任意字符串值,但对于整数值和较少数量的键,其存储结构更为优化。尽管Redis在网络数据库中速度较快,但相较于内存中的FreqDist,它的速度会明显变慢。不过,使用Redis可以获得数据的持久性和并发处理能力。
2. RedisConditionalHashFreqDist
- 准备工作 :需要安装Redis和redis - py,并启动redis - server实例。
-
实现步骤
:
-
在
redisprob.py中定义RedisConditionalHashFreqDist类,该类继承自nltk.probability.ConditionalFreqDist,并重写__getitem__()方法,以便创建RedisHashFreqDist实例而非FreqDist实例。
-
在
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。这种命名模式在初始化时用于查找所有现有的哈希映射。
3. RedisOrderedDict
- 准备工作 :同样需要安装Redis和redis - py,并启动redis - server实例。
-
实现步骤
:
rediscollections.py中的RedisOrderedDict类继承自collections.MutableMapping,实现了所有需要Redis有序集(Zset)命令的关键方法。
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()
-
工作原理
:该类的许多代码与
RedisHashMap相似,因为它们都继承自collections.MutableMapping。主要区别在于RedisOrderedSet按浮点值对键进行排序,因此不适用于像RedisHashMap那样的任意键值存储。以下是各关键方法及其与Redis的交互方式:
| 方法 | 功能 | Redis命令 |
| ---- | ---- | ---- |
|__len__()| 获取有序集中元素的数量 |zcard|
|__getitem__()| 获取键的分数,若键不存在则返回0 |zscore|
|__setitem__()| 向有序集添加键和分数,若键已存在则更新分数 |zadd|
|__delitem__()| 从有序集中移除键 |zrem|
|keys()| 获取有序集中按最高分数排序的所有键,可指定范围 |zrevrange|
|values()| 从items()方法中提取所有分数 | - |
|items()| 获取有序集中每个键的分数,返回按最高分数排序的二元组列表,可指定范围 |zrevrange|
|clear()| 从Redis中移除整个有序集 |delete|
二、分布式词评分
可以结合Redis和execnet进行分布式词评分。在之前的词评分计算中,使用了
FreqDist
和
ConditionalFreqDist
计算
movie_reviews
语料库中每个词的信息增益。现在可以使用
RedisHashFreqDist
和
RedisConditionalHashFreqDist
完成相同的任务,并将分数存储在
RedisOrderedDict
中。使用execnet可以分布式地进行计数,以提高Redis的性能。
1. 准备工作
需要安装Redis、redis - py和execnet,并在本地主机上运行redis - server实例。
2. 实现步骤
import itertools
import execnet
import 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
- 使用示例 :
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']]
3. 工作原理
-
score_words()函数的工作流程如下:- 打开网关和通道,向每个通道发送初始化数据。
-
通过通道发送每个
(label, words)元组进行计数。 -
向每个通道发送
done消息,等待done回复,然后关闭通道和网关。 -
根据计数计算每个词的分数,并存储在
RedisOrderedDict中。
-
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
该模块接收两种类型的数据:
-
done
消息:表示通道中没有更多数据,回复
done
消息并退出循环以关闭通道。
-
(label, words)
二元组:遍历该元组,在
RedisHashFreqDist
和
RedisConditionalHashFreqDist
中增加计数。
三、特定数据类型解析
在处理实际数据时,常常需要解析特定类型的数据,如日期、时间和HTML等。以下介绍使用相关库进行数据解析的方法。
1. 日期和时间解析
使用
dateutil
库可以方便地解析日期和时间。
-
准备工作
:可以使用
pip或easy_install安装dateutil,需要2.0版本以兼容Python 3。
sudo pip install dateutil==2.0
- 使用示例 :
from dateutil import parser
print(parser.parse('Thu Sep 25 10:36:28 2010')) # datetime.datetime(2010, 9, 25, 10, 36, 28)
print(parser.parse('Thursday, 25. September 2010 10:36AM')) # datetime.datetime(2010, 9, 25, 10, 36)
print(parser.parse('9/25/2010 10:36:28')) # datetime.datetime(2010, 9, 25, 10, 36, 28)
print(parser.parse('9/25/2010')) # datetime.datetime(2010, 9, 25, 0, 0)
print(parser.parse('2010-09-25T10:36:28Z')) # datetime.datetime(2010, 9, 25, 10, 36, 28, tzinfo=tzutc())
-
工作原理
:
dateutil的解析器不使用正则表达式,而是查找可识别的标记并尽力猜测其含义。标记的顺序很重要,例如不同文化使用的日期格式不同。parse()函数接受可选的关键字参数dayfirst和yearfirst来处理日期格式的差异。
print(parser.parse('25/9/2010', dayfirst=True)) # datetime.datetime(2010, 9, 25, 0, 0)
print(parser.parse('10-9-25')) # datetime.datetime(2025, 10, 9, 0, 0)
print(parser.parse('10-9-25', yearfirst=True)) # datetime.datetime(2010, 9, 25, 0, 0)
-
模糊解析
:
dateutil解析器还支持模糊解析,可忽略日期时间字符串中的无关字符。
try:
print(parser.parse('9/25/2010 at about 10:36AM'))
except ValueError:
print('cannot parse') # cannot parse
print(parser.parse('9/25/2010 at about 10:36AM', fuzzy=True)) # datetime.datetime(2010, 9, 25, 10, 36)
综上所述,Redis提供了多种强大的数据存储结构,结合execnet可以实现分布式处理,而
dateutil
等库则为特定数据类型的解析提供了便利。这些工具和技术在处理大规模数据和特定格式数据时具有重要的应用价值。
Redis与特定数据类型解析的综合应用
四、时区查找和转换
dateutil
库不仅能解析日期和时间,其
tz
模块还能进行时区的查找和转换。不过文档中未详细提及具体操作示例,我们可以推测在使用时,先导入
tz
模块,然后利用其中的函数和类来完成时区相关操作。例如,可能会有获取时区对象、将日期时间对象转换到指定时区等操作。以下是一个简单的推测示例(实际使用需根据
dateutil
的
tz
模块具体功能调整):
from dateutil import tz
# 获取一个时区对象,这里假设获取纽约时区
ny_tz = tz.gettz('America/New_York')
# 假设有一个日期时间对象
from dateutil import parser
dt = parser.parse('2024-01-01 12:00:00')
# 将日期时间对象转换到纽约时区
dt_ny = dt.replace(tzinfo=tz.UTC).astimezone(ny_tz)
print(dt_ny)
五、HTML数据处理
在处理HTML数据时,有多个库可以发挥作用,下面分别介绍它们的功能和使用方法。
1. 从HTML中提取URL
可以使用
lxml
库从HTML中提取URL。虽然文档未给出具体代码,但一般步骤如下:
- 安装
lxml
库:
pip install lxml
- 示例代码:
from lxml import html
import requests
# 获取HTML页面
url = 'https://example.com'
response = requests.get(url)
tree = html.fromstring(response.content)
# 提取所有链接
links = tree.xpath('//a/@href')
for link in links:
print(link)
2. 清理和剥离HTML
清理和剥离HTML可以去除HTML标签,只保留文本内容。文档未提及具体库,但常见的是使用
BeautifulSoup
库。
- 安装
BeautifulSoup
库:
pip install beautifulsoup4
- 示例代码:
from bs4 import BeautifulSoup
html = '<html><body><h1>Hello, World!</h1></body></html>'
soup = BeautifulSoup(html, 'html.parser')
text = soup.get_text()
print(text)
3. 转换HTML实体
BeautifulSoup
库也可以用于转换HTML实体。
from bs4 import BeautifulSoup
html = '<h1>Hello, World!</h1>'
soup = BeautifulSoup(html, 'html.parser')
decoded_html = soup.decode()
print(decoded_html)
4. 检测和转换字符编码
可以使用
charade
和
UnicodeDammit
库检测和转换字符编码。
- 安装
charade
库:
pip install charade
- 示例代码:
import charade
# 假设这是一个字节字符串
data = b'This is a sample text.'
result = charade.detect(data)
print(result['encoding'])
六、综合应用示例
假设需要解析一篇博客文章,提取文章文本、出站链接、日期和时间,并进行后续处理。可以将前面介绍的工具结合起来使用。
import requests
from lxml import html
from dateutil import parser
from bs4 import BeautifulSoup
import charade
# 获取博客文章页面
url = 'https://exampleblog.com/article'
response = requests.get(url)
tree = html.fromstring(response.content)
# 提取文章文本
article_text = tree.xpath('//article//text()')
article_text = ''.join(article_text)
# 提取出站链接
outbound_links = tree.xpath('//a[@href^="http"]/@href')
# 提取日期和时间
date_str = tree.xpath('//time/@datetime')[0]
date_obj = parser.parse(date_str)
# 清理文章文本中的HTML标签
soup = BeautifulSoup(article_text, 'html.parser')
clean_text = soup.get_text()
# 检测文章文本的字符编码
encoding = charade.detect(clean_text.encode())['encoding']
if encoding != 'utf-8':
clean_text = clean_text.encode(encoding).decode('utf-8')
# 后续可以使用NLTK进行词性标注、块提取或文本分类等操作
# 这里只是简单打印结果
print('文章文本:', clean_text)
print('出站链接:', outbound_links)
print('日期和时间:', date_obj)
七、总结
通过上述内容,我们了解了Redis的多种数据存储结构,包括
RedisHashMap
、
RedisConditionalHashFreqDist
和
RedisOrderedDict
,以及如何结合execnet进行分布式词评分。同时,掌握了使用
dateutil
解析日期和时间,以及使用
lxml
、
BeautifulSoup
、
charade
等库处理HTML数据和字符编码的方法。这些工具和技术在实际的数据处理和分析中具有重要的应用价值,可以帮助我们更高效地处理和利用各种类型的数据。
以下是一个简单的mermaid流程图,展示综合应用示例的流程:
graph TD;
A[获取博客文章页面] --> B[提取文章文本];
A --> C[提取出站链接];
A --> D[提取日期和时间];
B --> E[清理文章文本中的HTML标签];
E --> F[检测文章文本的字符编码];
F --> G{是否为utf-8};
G -- 否 --> H[转换为utf-8];
G -- 是 --> I[后续NLTK处理];
H --> I;
C --> I;
D --> I;
在实际应用中,可以根据具体需求对这些工具和技术进行灵活组合和调整,以满足不同的业务场景。
Redis与数据解析综合应用
超级会员免费看
1407

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



