Python爬虫-Redis

Python爬虫-Redis

前言

作为跟MongoDB同样NoSQL阵营的Redis,也具有类似的“直爽快”特性。它本身读取速度快,又提供丰富的数据结构,避免程序员重复造轮子。大名鼎鼎的分布式scrapy也是基于redis,所以赶紧了解一下呗!

启动服务器

建议指定配置文件的方式启动,我的配置文件所在路径: /etc/redis/redis-server.conf 就不知道你们的是不是啦

启动方式:redis-server /etc/redis/redis-server.conf

启动客户端

  1. 进入命令行:redis-cli
  2. 退出客户端:exit

数据操作

redis是key-value的数据,key的类型是字符串,value类型可以是:string,hash,list,set,zset

string

最基本类型,最大存储512M数据,可存储任何数据:数字,图片,序列化对象等


  1. 设置键值:set key value
    这里写图片描述
  2. 也可以一次设置多个数据:mset key1 value1 key2 value2 key3 value3...这里写图片描述
  3. 获取键值:get key;也可以一次获取多个键值:mget key1 key2 key3... 这里写图片描述
    如果不存在,返回nil
  4. 甚至还能追加键值:append key value
    这里写图片描述
  5. 也可以获取键值长度(有点像C语法哇):strlen key
    这里写图片描述
  6. 也可以在设置键值的时候设置过期时间:setex key seconds value
    这里写图片描述
  7. 运算(键值要求为数值):incr key 键值+1;incrby key increment 键值指定+increment ;相对应的就有decr key 键值-1,decrby key increment 指定-increment
    这里写图片描述

键命令

(不仅仅针对于string类型,其他value类型的key都适用)


  1. 查找键:keys pattern(支持正则),所以查看全部键可以是keys *
    这里写图片描述
  2. 查看键是否存在:exists key,存在返回1,不存在返回0
    这里写图片描述
    也可以一次查询多个键:exists key1 key2 ...,返回值为总和
    这里写图片描述
  3. 查看键的类型:type key
  4. 删除键:del key,成功返回1,失败或键不存在返回0;或者删除多个键:del key1 key2 ...,返回值为总和,即便键不存在,亦不会报错
    这里写图片描述
  5. 设置过期时间:expire key seconds;查看键的剩余存活时间:ttl key 这里写图片描述
    expire操作后返回1表示设置成功,返回0表示设置失败或是键不存在,设置时间单位秒;ttl操作后返回剩余存货时间,如果返回-2表示键不存在,如果返回-1代表永久存在
  6. 当然,也能清除过期时间: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,按照插入顺序排序,可列表的头或尾添加元素


  1. 从头/尾部插入:lpush/rpush key value,每次返回的值是列表中的元素个数
    这里写图片描述
  2. 在一个元素的前/后插入元素:linsert key before/after pivot value,这里的支点(pivot)就是原列表中的元素,value则是需要新添加的元素
    这里写图片描述
    失败则返回-1
  3. 移除并获得值:lpop/rpop key,这里可以用python的list类型的pop方法来理解
    这里写图片描述
  4. 利用索引获取元素:lindex key index
    这里写图片描述
  5. 获取key的长度(也就是列表中的元素个数):llen key
  6. 修改列表中的元素值:lset key index value,指定元素在列表中的索引(index),value是修改后的内容。==如果index值上并不存在元素,报索引错误==
  7. 返回指定范围的元素:lrange key startIndex stopIndex
    这里写图片描述
    超出索引范围不报错
  8. 裁剪列表:ltrim key startIndex stopIndex

set

无序集合,元素类型string,元素具有唯一性,不重复


  1. 添加元素:sadd key member;或者一次添加多个:sadd key member1 member2 ...,如果添加的元素已存在,返回0
    这里写图片描述
  2. 移除元素:srem key member / srem key member1 member2 ...
  3. 获取所有元素:smembers key
  4. 获取集合元素个数:scard key
  5. 求多个集合的交集:sinter key1 key2 ...
  6. 求集合与其他集合的差集:sdiff key1 key2 ...
  7. 求多个集合的合集:sunion key1 kye2 ...
  8. 判断元素是否在集合中:sismember key member,存在返回1,不存在返回0
    这里写图片描述

zset

有序集合,唯一性,每个元素都会关联到一个double类型的score,表示权重,根据权重对元素排序,元素的score可以相同


  1. zadd key score memberzadd key socre1 member1 score2 member2 ...
  2. zrem key member/zrem key member1 member2 ...
  3. zrange key start stop
  4. zcard key
  5. 统计score值在min与max的个数:zcount key min max
  6. 返回member的score值:zscore key member

发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

  1. 消息格式:
    a. subscribe 频道名1 [频道名2 ...] 订阅
    b. unsubscribe 频道名1 [频道名2 ...] 退订
    c. message之后显示频道,再之后显示正文
    d. publish 频道 消息 发布

发布消息
这里写图片描述

接受消息
这里写图片描述

主从配置

每个主都可以设置许多的从,每个从又可以设置许多的从;通过设置主从,搭建分布式,如scrapy-redis分布式爬虫

  1. 对主机修改配置文件:bind 主机Ip
  2. 对从机修改配置文件:
    bind 从机Ip
    slaveof 主机Ip port

与python交互

  1. 安装包redis,导入:import redis
  2. 连接redis:r = redis.StrictRedis(host="hostname", prot=6379)
  3. 使用方法1:根据数据类型,使用对应方法,如:r.set("name", "kaihui") / r.get("name")
  4. 使用方法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的开源项目差之千里
这里写图片描述

想起一晚趴在床上看《爱你就像爱生命》,有多少人知道王小波也是个程序员呢?他在给朋友晓阳的信中这样写道:我的图像部分也是汇编写的,反复优化,也达不到他的水平,不得不承认技不如人。

路漫漫其修远兮呢!

posted @ 2018-07-22 07:52  有关心情  阅读(1823)  评论(3编辑  收藏  举报