前言
作为跟MongoDB同样NoSQL阵营的Redis,也具有类似的“直爽快”特性。它本身读取速度快,又提供丰富的数据结构,避免程序员重复造轮子。大名鼎鼎的分布式scrapy也是基于redis,所以赶紧了解一下呗!
启动服务器
建议指定配置文件的方式启动,我的配置文件所在路径: /etc/redis/redis-server.conf
就不知道你们的是不是啦
启动方式:redis-server /etc/redis/redis-server.conf
启动客户端
- 进入命令行:
redis-cli
- 退出客户端:
exit
数据操作
redis是key-value的数据,key的类型是字符串,value类型可以是:string,hash,list,set,zset
想要学习Python?Python学习交流群:973783996满足你的需求,资料都已经上传群文件,可以自行下载!
string
最基本类型,最大存储512M数据,可存储任何数据:数字,图片,序列化对象等
- 设置键值:
set key value
- 也可以一次设置多个数据:
mset key1 value1 key2 value2 key3 value3...
- 获取键值:
get key
;也可以一次获取多个键值:mget key1 key2 key3...
如果不存在,返回nil - 甚至还能追加键值:
append key value
- 也可以获取键值长度(有点像C语法哇):
strlen key
- 也可以在设置键值的时候设置过期时间:
setex key seconds value
- 运算(键值要求为数值):
incr key
键值+1;incrby key increment
键值指定+increment ;相对应的就有decr key
键值-1,decrby key increment
指定-increment
键命令
(不仅仅针对于string类型,其他value类型的key都适用)
- 查找键:
keys pattern
(支持正则),所以查看全部键可以是keys *
- 查看键是否存在:
exists key
,存在返回1,不存在返回0
也可以一次查询多个键:exists key1 key2 ...
,返回值为总和 - 查看键的类型:
type key
- 删除键:
del key
,成功返回1,失败或键不存在返回0;或者删除多个键:del key1 key2 ...
,返回值为总和,即便键不存在,亦不会报错 - 设置过期时间:
expire key seconds
;查看键的剩余存活时间:ttl key
expire操作后返回1表示设置成功,返回0表示设置失败或是键不存在,设置时间单位秒;ttl操作后返回剩余存货时间,如果返回-2表示键不存在,如果返回-1代表永久存在 - 当然,也能清除过期时间:
persist key
如果persit操作之后返回1表示操作成功;如果返回0,键不存在或键本身就是永久
hash
用于存储对象,对象格式为键值对
1. hset key field value / hmset key field1 value1 field2 value2 ...
,如何理解”对象”呢?即:一个人,这就是一个对象,有名字,年龄,性别等
2. hget key field / hmget key field1 field2 ...
3. 获取指定key的所有字段和值: hgetall key
4. 获取指定key的所有字段:hkeys key
5. 获取指定key的所有值:hvals key
6. 获取指定key的字段个数:hlen key
7. 判断key的字段是否存在:hexists key field
,存在返回1,不存在返回0
8. 删除字段及对应值:hdel key field / hdel key field1 field2 ...
9. 删除key:del key
10. 获取值的字符串长度:hstrlen key field
我查了使用文档,的确存在这个hstrlen命令,用Tab命令提示也能自动弹出来,但是——
哈哈,布吉岛布吉岛,先占位以后填坑吧
list
列表的元素类型是string,按照插入顺序排序,可列表的头或尾添加元素
- 从头/尾部插入:
lpush/rpush key value
,每次返回的值是列表中的元素个数 - 在一个元素的前/后插入元素:
linsert key before/after pivot value
,这里的支点(pivot)就是原列表中的元素,value则是需要新添加的元素
失败则返回-1 - 移除并获得值:
lpop/rpop key
,这里可以用python的list类型的pop方法来理解 - 利用索引获取元素:
lindex key index
- 获取key的长度(也就是列表中的元素个数):
llen key
- 修改列表中的元素值:
lset key index value
,指定元素在列表中的索引(index),value是修改后的内容。==如果index值上并不存在元素,报索引错误== - 返回指定范围的元素:
lrange key startIndex stopIndex
超出索引范围不报错 - 裁剪列表:
ltrim key startIndex stopIndex
set
无序集合,元素类型string,元素具有唯一性,不重复
- 添加元素:
sadd key member
;或者一次添加多个:sadd key member1 member2 ...
,如果添加的元素已存在,返回0 - 移除元素:
srem key member / srem key member1 member2 ...
- 获取所有元素:
smembers key
- 获取集合元素个数:
scard key
- 求多个集合的交集:
sinter key1 key2 ...
- 求集合与其他集合的差集:
sdiff key1 key2 ...
- 求多个集合的合集:
sunion key1 kye2 ...
- 判断元素是否在集合中:
sismember key member
,存在返回1,不存在返回0
zset
有序集合,唯一性,每个元素都会关联到一个double类型的score,表示权重,根据权重对元素排序,元素的score可以相同
zadd key score member
;zadd key socre1 member1 score2 member2 ...
zrem key member/zrem key member1 member2 ...
zrange key start stop
zcard key
- 统计score值在min与max的个数:
zcount key min max
- 返回member的score值:
zscore key member
发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
- 消息格式:
a.subscribe 频道名1 [频道名2 ...]
订阅
b.unsubscribe 频道名1 [频道名2 ...]
退订
c.message之后显示频道,再之后显示正文
d.publish 频道 消息
发布
发布消息
接受消息
主从配置
每个主都可以设置许多的从,每个从又可以设置许多的从;通过设置主从,搭建分布式,如scrapy-redis分布式爬虫
- 对主机修改配置文件:
bind 主机Ip
- 对从机修改配置文件:
bind 从机Ip
slaveof 主机Ip port
与python交互
- 安装包redis,导入:
import redis
- 连接redis:
r = redis.StrictRedis(host="hostname", prot=6379)
- 使用方法1:根据数据类型,使用对应方法,如:
r.set("name", "kaihui") / r.get("name")
- 使用方法2:
p = r.pipeline()
p.set(...)
p.get(...)
p.execute()
==法2缓冲多条命令,然后一次性执行,减少服务器-客户端之间TCP数据库包,从而提高效率==
之前在redis客户端操作的命令,都很好的封装到了连接数据库的对象里,可以直接使用,如:r.hset() r.sadd()…
利用redis实战
之前抓取过电影排行榜,句子迷,QQ空间,音乐热评等等,其实大多操作类似,今天想换别的方向(其实操作也类似)。
众所周知,爬虫是容易封ip的,为了应对如此强硬的反扒措施,有了利用代理ip爬取信息的方法。既然有了需求,当然就有了市场,我也没非要不可的数据需要爬取,便不至于花钱购买代理。
西刺网站提供免费代理,既然有“免费”二字,效果大家就应该心知肚明的。或许人公司提供的效果不至于那么差,却奈何全国爬虫都想“好好”利用。
分析网页一如既往,这个网页毫无难度,requests发起get请求,利用xpath解析响应,提取ip和端口号,再存入redis。稍微不同的是,我们拿到的代理ip能不能用呢?这里需要做一个测试,我写了一个filter_ip()函数进行过滤,主要思想是利用代理去访问百度,如果返回状态码是200,那么OK,存起来;否则丢弃
def filter_ip(proxyData):
# 剔除https类型的
if proxyData["type"].upper() == "HTTPS":
return
del proxyData["type"]
# 构造requests模块ip代理的参数结构
proxyData = {
"http":"http://"+proxyData["ip"]+":"+proxyData["port"]
}
try:
response = requests.get(url="http://www.baidu.com/", proxies=proxyData)
response.raise_for_status()
except:
print(f"{proxyData}不可用")
return None
# 若可用,存入redis
to_redis(proxyData)
由于构建ip池只是为了更好的辅助爬虫,所以效率方面要求严格,为此我利用了多进程+多线程来达到目的
# 设置进程池
pool = Pool(10)
for item in parse_html(html):
pool.apply_async(set_threading, (item, ))
pool.close()
pool.join()
def set_threading(item):
# 设置线程函数
t = Thread(target=filter_ip, args=(item, ))
t.start()
t.join()
整体逻辑如下
反思
我抓取了代理网页前三页,然而仅9个写入数据库,其免费可想而知
最初写的单进程+单线程,运行速度极慢,才想到多进程+多线程。多番调试,速度提升了6倍。然而也不过对300个ip处理而已,竟需要200s上下。与GitHub上最高star的开源项目差之千里