運用基於內存的數據庫redis構建分佈式爬蟲–抓妹子圖網

  • 2019 年 11 月 27 日
  • 筆記

當你能夠針對一個url進行請求,獲取數據,繼續請求的時候,說明你的爬蟲已經可以自給自足的爬起來。但是這樣的爬蟲其效率將會嚴重限制在單進程效率極限之下,時間的主要消耗還是在請求返回的等待時間,如果想進一步提高效率那麼多進程以及分佈式就會你提高效率的最好手段。而且分佈式並不意味着你一定要很多台電腦,只要你在本機測試通過一樣可以方便遷移。構建分佈式爬蟲主要是構建分佈式環境,至於寫爬蟲並不複雜。咱們一步步來。

小怪獸鎮樓

目錄:

  1. redis簡介
  2. 安裝測試
  3. 多機測試
  4. scrapy_redis
  5. 簡單應用測試
  6. 多機協作的redis

1.redis簡介:

redis是一個key-value存儲系統,速度很快因為將數據存放在內存,在必要時可以轉到硬盤。支持數據類型有string,lists,sets,zsets。這些數據類型都支持push/pop,add/remove以及取交集並集差集等操作,對這些操作都是原子性的,redis還支持各種不同的排序能力。

redis本身屬於一個數據庫類型的系統,不過在分佈式中反而是他的隊列性特別好用,就被開發成分佈式的基石。所以今天我們測試的內容就是在多台機器上安裝redis,然後讓一台作為服務器別的機器開啟客戶端共享隊列。

2.安裝測試:

我就不搬磚了。下面的鏈接非常詳細(重點博文):

http://blog.fens.me/linux-redis-install/

還有redis有大量的命令,詳細可以查詢此處

http://www.3fwork.com/b902/001557MYM005172/

還有修改配置打開遠程訪問之類的

http://www.ttlsa.com/redis/install-redis-on-ubuntu/

3.多機測試:

使用你的服務器(有遠程服務器最好,沒有的話也可以在自己本機安裝),按照上面安裝好環境後,先去除保護的開啟服務器端(未來長久使用要改配置)

如果報錯,請直接改配值,將127.0.0.1注釋掉(系統的防火牆要關)

redis-server --protected-mode no

然後先再開一個終端鏈接到服務器,在服務器本機的環境也開一個客戶端,(只是因為機器不多)

redis-cli -h ip地址 -p 6379

回車之後會看到進入一個交互命令行界面。

首先增加一個key對應的value

set key1 lala

其次查看這個值

get key1

然後返回你的個人電腦,也通過客戶端鏈接過去然後查看這個值

get key1

如果出現同樣的lala。那麼說明分佈式的環境已經搭建好了。

4.scrapy_redis

python的爬蟲框架scrapy也可以使用redis.請看這裡:

https://www.zhihu.com/question/32302268/answer/55724369

本次的測試項目就是運用了一個共享隊列而已,還沒有增加別的多種功能。

因為scrapy——redis使用起來需要環境配置的比較麻煩,還得改寫其中中間件,想進一步學習在scrapy中用redis可以再找文章。

5.簡單應用測試,分佈式抓妹子圖網

一般而言分佈式至少需要兩個程序:完整代碼我放在github上:https://github.com/luyishisi/WebCrawlers/tree/master/request

一個用於捕捉待爬隊列,並且加入到隊列。如果待爬隊列可以通過一定的規律算出來則可以輕鬆直接push到隊列中

另外一個則是讀取待爬隊列進行新一輪的爬取工作

要實現捕捉待爬隊列並且加入見下代碼:

1:頭文件:

requets用於發請求,re正則匹配,time控制速度,redis分佈式。headers用於簡單的反反爬

import requests  import re  import time  from redis import Redis  headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36' }

2:加任務隊列

計算並發送請求獲取根頁面的html從中匹配到img的地址發送到本地redis中。

def push_redis_list():      r = Redis()      print r.keys('*')      for i in range(100):          num = 5100+i;#抓取的取件僅在5100--5200之間          url ='http://www.meizitu.com/a/'+ str(num) +'.html'          img_url = requests.get(url,timeout=30)          #print img_url.text          #time.sleep(10)          img_url_list = re.findall('http://pic.meizitu.com/wp-content/uploads/201.*.jpg',img_url.text)          print img_url_list          for temp_img_url in img_url_list:              l = len(re.findall('limg',temp_img_url))              #print l              if(l == 0):                  print "url: ",temp_img_url                  r.lpush('meizitu',temp_img_url)          print r.llen('meizitu')      return 0

3:讀任務隊列

def get_big_img_url():      r = Redis()      print r.keys('*')      while(1):          try:              url = r.lpop('meizitu')              download(url)              time.sleep(1)              print url          except:              print "請求求發送失敗重試"              time.sleep(10)              continue      return 0

4:download函數用於下載

def download(url):      try:          r = requests.get(url,headers=headers,timeout = 50)          name = int(time.time())          f = open('./pic/'+str(name)+'.jpg','wb')          f.write(r.content)          f.close()      except Exception,e:          print Exception,":",e

5:main

開一個客戶端運行push_redis_list 函數,別的可以開啟任意多個客戶端運行get_big_img_url 函數,則簡單的分佈式就構建完成

if __name__ == '__main__':      url = 'http://www.meizitu.com/a/list_1_'      print "begin"      #push_redis_list(5100)#開啟則加任務隊列.其中的值請限制在5400以內。不過是用於計算頁碼的      #get_big_img_url()#開啟則運行爬取任務

運行的過程中你可以看一下該文件夾下是否有一個pic的文件夾,所有圖片存儲其中。

運行截圖:圖一加入任務隊列

圖二:單個客戶端進行讀取任務隊列進行爬取

圖三:截個圖,比較模糊應該不會查水表吧。

6:多機協作的redis

要開啟多機合作的redis分佈式,也並不困難,最好將redis服務器放置在某服務器上,有固定ip。同時設定好安全密碼或者固定的ip白名單,redis直接開放很容易被黑了。

然後修改代碼建立redis連接的部分,。如下:

    r=Redis(host=REDIS_HOST, port=REDIS_PORT,password=PASSWORD)

然後便是依舊一樣的使用。

附錄:

redis命令大全

redis常用操作命令 操作相關的命令連接 quit:關閉連接(connection) auth:簡單密碼認證 持久化 save:將數據同步保存到磁盤 bgsave:將數據異步保存到磁盤 lastsave:返回上次成功將數據保存到磁盤的Unix時戳 shundown:將數據同步保存到磁盤,然後關閉服務 遠程服務控制 info:提供服務器的信息和統計 monitor:實時轉儲收到的請求 slaveof:改變複製策略設置 config:在運行時配置Redis服務器 對value操作的命令 exists(key):確認一個key是否存在 del(key):刪除一個key type(key):返回值的類型 keys(pattern):返回滿足給定pattern的所有key randomkey:隨機返回key空間的一個 keyrename(oldname, newname):重命名key dbsize:返回當前數據庫中key的數目 expire:設定一個key的活動時間(s) ttl:獲得一個key的活動時間 select(index):按索引查詢 move(key, dbindex):移動當前數據庫中的key到dbindex數據庫 flushdb:刪除當前選擇數據庫中的所有key flushall:刪除所有數據庫中的所有ke 對String操作的命令 set(key, value):給數據庫中名稱為key的string賦予值value get(key):返回數據庫中名稱為key的string的value getset(key, value):給名稱為key的string賦予上一次的value mget(key1, key2,…, key N):返回庫中多個string的value setnx(key, value):添加string,名稱為key,值為value setex(key, time, value):向庫中添加string,設定過期時間time mset(key N, value N):批量設置多個string的值 msetnx(key N, value N):如果所有名稱為key i的string都不存在 incr(key):名稱為key的string增1操作 incrby(key, integer):名稱為key的string增加integer decr(key):名稱為key的string減1操作 decrby(key, integer):名稱為key的string減少integer append(key, value):名稱為key的string的值附加value substr(key, start, end):返回名稱為key的string的value的子串 對List操作的命令 rpush(key, value):在名稱為key的list尾添加一個值為value的元素 lpush(key, value):在名稱為key的list頭添加一個值為value的 元素 llen(key):返回名稱為key的list的長度 lrange(key, start, end):返回名稱為key的list中start至end之間的元素 ltrim(key, start, end):截取名稱為key的list lindex(key, index):返回名稱為key的list中index位置的元素 lset(key, index, value):給名稱為key的list中index位置的元素賦值 lrem(key, count, value):刪除count個key的list中值為value的元素 lpop(key):返回並刪除名稱為key的list中的首元素 rpop(key):返回並刪除名稱為key的list中的尾元素 blpop(key1, key2,… key N, timeout):lpop命令的block版本。 brpop(key1, key2,… key N, timeout):rpop的block版本。 rpoplpush(srckey, dstkey):返回並刪除名稱為srckey的list的尾元素,並將該元素添加到名稱為dstkey的list的頭部 對Set操作的命令 sadd(key, member):向名稱為key的set中添加元素member srem(key, member) :刪除名稱為key的set中的元素member spop(key) :隨機返回並刪除名稱為key的set中一個元素 smove(srckey, dstkey, member) :移到集合元素 scard(key) :返回名稱為key的set的基數 sismember(key, member) :member是否是名稱為key的set的元素 sinter(key1, key2,…key N) :求交集 sinterstore(dstkey, (keys)) :求交集並將交集保存到dstkey的集合 sunion(key1, (keys)) :求並集 sunionstore(dstkey, (keys)) :求並集並將並集保存到dstkey的集合 sdiff(key1, (keys)) :求差集 sdiffstore(dstkey, (keys)) :求差集並將差集保存到dstkey的集合 smembers(key) :返回名稱為key的set的所有元素 srandmember(key) :隨機返回名稱為key的set的一個元素 對Hash操作的命令 hset(key, field, value):向名稱為key的hash中添加元素field hget(key, field):返回名稱為key的hash中field對應的value hmget(key, (fields)):返回名稱為key的hash中field i對應的value hmset(key, (fields)):向名稱為key的hash中添加元素field hincrby(key, field, integer):將名稱為key的hash中field的value增加integer hexists(key, field):名稱為key的hash中是否存在鍵為field的域 hdel(key, field):刪除名稱為key的hash中鍵為field的域 hlen(key):返回名稱為key的hash中元素個數 hkeys(key):返回名稱為key的hash中所有鍵 hvals(key):返回名稱為key的hash中所有鍵對應的value hgetall(key):返回名稱為key的hash中所有的鍵(field)及其對應的value

原創文章,轉載請註明: 轉載自URl-team

本文鏈接地址: 運用基於內存的數據庫redis構建分佈式爬蟲–抓妹子圖網

  1. Scrapy筆記三 自動多網頁爬取-本wordpress博客所有文章
  2. Scrapy筆記四 自動爬取網頁之使用CrawlSpider
  3. 基於百度IP定位的網站訪問來源分析的python實戰項目–實踐筆記零–項目規劃
  4. SCRAPY學習筆記九 增量爬取url 使用 yield 的用法
  5. 爬蟲破解IP限制–ADSL動態IP服務器–部署小結
  6. Redis大批量上傳數據 使用shell與python腳本