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的開源項目差之千里
這里寫圖片描述

想起一晚趴在床上看《愛你就像愛生命》,有多少人知道王小波也是個程序員呢?他在給朋友曉陽的信中這樣寫道:我的圖像部分也是匯編寫的,反復優化,也達不到他的水平,不得不承認技不如人。

路漫漫其修遠兮呢!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM