摘要
就在前几个月,我在使用爬虫框架Scrapy对亚马逊的香水信息进行采集时,亚马逊的反爬机制让我的程序开启后的半个小时内强制的停止下来(估计是被识别了 程序没嘣前还挺兴奋的说 亚马逊反爬真是太垃圾了,我连代理池都不需要设置)
但是识别了很正常,我并没在意,只需要加入代理池,我随意的拿出其中一个去请求就完事了,在一系列配置之后,我需要另外的去重新请求,此刻我的疑惑就来了?我前面也确实拿到亚马逊的数据,难不成我还得重新请求再花上半小时的时间再来一遍?万一后面的数据又有重复呢?
不过当时我没有去细抛,因为我的任务完成了 。但是这几天,我听别人说起了分布式爬虫以及Redis之后。
我对数据定点爬取和分布式爬虫有了新的了解,类似的问题 我有8成的信心 可以引刃而解。总之一个结论,亚马逊的反爬策略现在我面前就是个屁
笔者今天就先带你入个门 -Redis的基本使用
Redis
抛出一个疑问 我们通过百度引擎搜索 他就是从数据库查出来的吗?
我们先来聊一聊NoSQL 大致分为四类 ,我们的redis 属于 k-v存储库
他用到了哈希表 通过特定的Key和一个指针指向特定的数据 虽然简单易部署
但是我们的DBA只要部分值,我们就把他缓存起来 就显得有点低下了
介绍
redis是业界主流的key-value nosql 数据库之一。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
重点:持久化,支持5大基础数据类型,分布式,分布式锁,自动过期能有效提升开发效率
优点:异常快速适合当做缓存,5大数据类型,原子性操作,
Linux安装Redis环境
wget http://download.redis.io/releases/redis-3.0.6.tar.gz
tar xzf redis-3.0.6.tar.gz
cd redis-3.0.6
make
$sudo apt-get update
$sudo apt-get install redis-server
启动 Redis: $redis-server
启动:redis-cli
python下使用redis
pip3 install redis
import redis
r = redis.Redis() #默认 host='127.0.0.1', port=6379
r.set('foo', 'Bar')
print(r.get('foo'))
我们也可以加入连接池,这样多个
疑惑?连接池是共享数据用的还 是独立的?他是为了减缓连接压力吗?
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)
r.set('foo', 'Bar')
print(r.get('foo'))
redis事务
conn=Redis() pipe = conn.pipeline(transaction=True) pipe.multi() pipe.set('name', 'alex') pipe.set('role', 'sb') pipe.execute()
Django中使用redis
-在setting中配置:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "123",}
}
}
from django_redis import get_redis_connection conn=get_redis_connection() conn.set
Redis设置字符串
1.set
set(name, value[ex=None, px=None, nx=False, xx=False]) #设置键值
ex 过期时间:秒
px 过期时间:毫秒
nx True:只添加
xx True:只修改
2.psetex
psetex(name, time_ms, value) #支持timedelta对象
3.mset
mset(*args, **kwargs)批量设置
4..get
get(name)
取值
5.mget
mget(keys, *args) 批量获取
或者 mget(["k1","v2"])
6.getset
getset(name, value) #重新设置值 并把原值返回
7.getrange
getrange(key, start, end)
start 起始位置
end 结束位置
8.setrange
setrange(name,offset,value)
offset 一个汉字三个字节
value 设置的值
9.setbit
setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作
# 参数:
# name,redis的name
# offset,位的索引(将值变换成二进制后再进行索引)
# value,值只能是 1 或 0
# 注:如果在Redis中有一个对应: n1 = "foo",
那么字符串foo的二进制表示为:01100110 01101111 01101111
所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
那么最终二进制则变成 01100111 01101111 01101111,即:"goo
getbit(name, offset)
# 获取name对应的值的二进制表示中的某位的值 (0或1)
bitcount(key, start=None, end=None)
# 获取name对应的值的二进制表示中 1 的个数
# 参数:
# key,Redis的name
# start,位起始位置
# end,位结束位置
bitop(operation, dest, *keys)
# 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
# 参数:
# operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
# dest, 新的Redis的name
# *keys,要查找的Redis的name
# 如:
bitop("AND", 'new_name', 'n1', 'n2', 'n3')
# 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中
strlen(name)
# 返回name对应值的字节长度(一个汉字3个字节)
incr(self, name, amount=1)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
# name,Redis的name
# amount,自增数(必须是整数)
# 注:同incrby
incrbyfloat(self, name, amount=1.0)
同上
decr(self, name, amount=1)
自减
append(key, value)
字符串追加
Hash操作
hset(name, key, value)
不存在创建 存在修改
hmset(name, mapping)
同理
hget(name,key)
略
hmget(name, keys, *args)
hmget(name,['k1', 'k2'])
hmget(name,['k1', 'k2'])
hgetall(name) hkeys(name) hvals(name)
获取所有 键值对 |获取所有key |获取所有value
hlen(name)
获取长度
hexists(name, key)
存在1 不存在0
hdel(name,*keys)
hdel("sda",1,2,3,4,5)
hincrby #自增key
hincrby (name, key, amount=1) hincrbyfloat(name, key, amount=1.0)
自增的key
hscan #迭代器里了没有 再去数据库里取
hscan(name, cursor=0, match=None, count=None)
hscan_iter #
(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
# 参数:
``# match,匹配指定key,默认None 表示所有的key
``# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
``# for item in r.hscan_iter('xx'):
``# print item
list
lpush 左放
(name,values) r.lpush('oo', 11,22,33)
lpushx(name,value) Rpushx
左添加
llen 求长度
(name)
linsert 在标杆出前后插入
(name, where, refvalue, value))
where BEFORE或AFTER
refvalue 标杆值 寻找第一个数
value 要插入的值
r.lset 替换
(name, index, value)
r.lrem # 删除的返回
(name, value, num)
0 删除全部value
整数 从头删多少个
负数 从后删多少个
lpop右边弹出 返回
(name)
lindex #根据索引获取列表元素
(name, index)
lrange #切片得到数据
(name, start, end)
ltrim(name, start, end) 只保留范围内规定的值
rpoplpush 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
(src, dst)
blpop 没有了会阻塞
(keys, timeout)
brpoplpush 同上上 阻塞
(src, dst, timeout=0)
set
有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
zadd(name, *args, **kwargs)
# 在name对应的有序集合中添加元素``# 如:`` ``# zadd('zz', 'n1', 1, 'n2', 2)`` ``# 或`` ``# zadd('zz', n1=11, n2=22)
zcard(name)
# 获取name对应的有序集合元素的数量
zcount(name, min, max)
# 获取name对应的有序集合中分数 在 [min,max] 之间的个数
zincrby(name, value, amount)
# 自增name对应的有序集合的 name 对应的分数
r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
# 按照索引范围获取name对应的有序集合的元素` `# 参数:`` ``# name,redis的name`` ``# start,有序集合索引起始位置(非分数)`` ``# end,有序集合索引结束位置(非分数)`` ``# desc,排序规则,默认按照分数从小到大排序`` ``# withscores,是否获取元素的分数,默认只获取元素的值`` ``# score_cast_func,对分数进行数据转换的函数` `# 更多:`` ``# 从大到小排序`` ``# zrevrange(name, start, end, withscores=False, score_cast_func=float)` ` ``# 按照分数范围获取name对应的有序集合的元素`` ``# zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)`` ``# 从大到小排序`` ``# zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)
zrank(name, value)
# 获取某个值在 name对应的有序集合中的排行(从 0 开始)` `# 更多:`` ``# zrevrank(name, value),从大到小排序
zrem(name, values)
# 删除name对应的有序集合中值是values的成员` `# 如:zrem('zz', ['s1', 's2'])
zremrangebyrank(name, min, max)
# 根据排行范围删除
zremrangebyscore(name, min, max)
# 根据分数范围删除``
zscore(name, value)
# 获取name对应有序集合中 value 对应的分数
zinterstore(dest, keys, aggregate=None)
# 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作``# aggregate的值为: SUM MIN MAX
zunionstore(dest, keys, aggregate=None)
# 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作``# aggregate的值为: SUM MIN MAX
zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)
# 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作