天天看點

Scrapy架構學習(八)----Scrapy-redis分布式爬蟲學習

Scrapy架構學習(八)—-Scrapy-redis分布式爬蟲學習

Scrapy-redis

分布式爬蟲架構,是在

Scrapy

爬蟲架構的基礎上進行改進的,通過

Redis

來進行資料的緩存,可以在多台機器上運作爬蟲程式。本文示例是在

CentOS

的虛拟機運作。

1、Redis安裝

關于Redis的安裝,網上有不少的文章,在配置Redis環境上也會有些問題,下面的2篇文章,詳細的介紹了Redis的安裝和問題,這裡就直接搬運過來了。因為我的

Scrapy-Redis

的爬蟲代碼,是運作在

CentOS

CentOS 7下Redis的安裝與配置

CentOS 7.0 安裝Redis 3.2.1詳細過程和使用常見問題

2、Redis分布式測試

我們采用3台機器,進行分布式爬蟲爬取資料,在虛拟機上安裝好

Redis

後,我們來進行

Redis

的分布式測試。

我們将server1作為

Master

機器(

ip:192.168.108.20

)。server2(

ip:192.168.108.21

),server3(

ip:192.168.108.22

)作為2台機器為

Slaves

2.1 啟動Master機器的Redis服務

找到redis的配置檔案

redis.conf

,注釋

bind 127.0.0.1

,保護模式

protected-mode

設定為

no

指定

redis.conf

啟動

redis-server /redis/redis-stable/redis.conf
           

如圖:

Scrapy架構學習(八)----Scrapy-redis分布式爬蟲學習

2.2 啟動每台機器的Redis用戶端redis-cli

Master機器:

Scrapy架構學習(八)----Scrapy-redis分布式爬蟲學習

Slaves機器,指定Master機器的ip

如果出現如下情況:

Could not connect to Redis at :: No route to host
           
Scrapy架構學習(八)----Scrapy-redis分布式爬蟲學習

需要對每台機器清除下防火牆配置:

sudo iptables -F
           

再次連接配接Master的Redis,如圖:

Scrapy架構學習(八)----Scrapy-redis分布式爬蟲學習

如果機器連接配接不通,可能是防火牆的問題,可以試試:sudo iptables -F 清除防火牆配置

2.3 測試分布式Redis

Scrapy架構學習(八)----Scrapy-redis分布式爬蟲學習

3、scrapy-redis的爬蟲代碼

Scrapy-redis分布式爬蟲,爬取的是:

http://sh.58.com/chuzu/

下面我們看下代碼,基本和Scrapy的項目一樣,就Spider的類改為Scrapy-redis中的類,settings.py中的配置不一樣。

3.1 Item

定義實體類

class Redis58TestItem(scrapy.Item):
    # 标題
    title = scrapy.Field()
    # 房間
    room = scrapy.Field()
    # 區域
    zone = scrapy.Field()
    # 位址
    address = scrapy.Field()
    # 價格
    money = scrapy.Field()
    # 釋出資訊的類型,品牌較高價的電梯大廈,經紀人,個人
    type = scrapy.Field()
           

3.2 spider

spider爬蟲類實作

from scrapy.spiders import Rule
from scrapy_redis.spiders import RedisCrawlSpider
from scrapy.linkextractors import LinkExtractor
from redis58test.items import Redis58TestItem


class Redis58Spider(RedisCrawlSpider):# 繼承的Spider是Scrapy-redis架構提供的
    # spider的唯一名稱
    name = 'redis58spider_redis'
    # 開始爬取的url
    redis_key = 'redis58spider:start_urls'
    # 從頁面需要提取的url 連結(link)
    links = LinkExtractor(allow="sh.58.com/chuzu/pn\d+")
    # 設定解析link的規則,callback是指解析link傳回的響應資料的的方法
    rules = [Rule(link_extractor=links, callback="parseContent", follow=True)]

    def parseContent(self, response):
        """
        解析響應的資料,擷取需要的資料字段
        :param response: 響應的資料
        :return:
        """
        # 根節點 //ul[@class="listUl"]/li[@logr]
        # title: .//div[@class="des"]/h2/a/text()
        # room: .//div[@class="des"]/p[@class="room"]/text()
        # zone: .//div[@class="des"]/p[@class="add"]/a[1]/text()
        # address: .//div[@class="des"]/p[@class="add"]/a[last()]/text()
        # money: .//div[@class="money"]/b/text()
        # type: # .//div[@class="des"]/p[last()]/@class     # 如果是add,room  .//div[@class="des"]/div[@class="jjr"]/@class

        for element in response.xpath('//ul[@class="listUl"]/li[@logr]'):
            title = element.xpath('.//div[@class="des"]/h2/a/text()')[].extract().strip()
            room = element.xpath('.//div[@class="des"]/p[@class="room"]')[].extract()
            zone = element.xpath('.//div[@class="des"]/p[@class="add"]/a[1]/text()')[].extract()
            address = element.xpath('.//div[@class="des"]/p[@class="add"]/a[last()]/text()')[].extract()
            money = element.xpath('.//div[@class="money"]/b/text()')[].extract()
            type = element.xpath('.//div[@class="des"]/p[last()]/@class')[].extract()
            if type == "add" or type == "room":
                type = element.xpath('.//div[@class="des"]/div[@class="jjr"]/@class')[].extract()

            item = Redis58TestItem()

            item['title'] = title
            item['room'] = room
            item['zone'] = zone
            item['address'] = address
            item['money'] = money
            item['type'] = type

            yield item
           

3.3 Pipeitem

實際上我們不需要處理Pipeitem的資料,因為Scrapy架構會将資料給redis去重。

class Redis58TestPipeline(object):
    def process_item(self, item, spider):
        return item
           

3.4 settings.py設定

配置Scrapy-Redis的資訊

DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True

# 使用什麼隊列排程
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"    # 優先級隊列
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"  # 基本隊列
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"  # 棧

ITEM_PIPELINES = {
    'redis58test.pipelines.Redis58TestPipeline': ,
    'scrapy_redis.pipelines.RedisPipeline': ,
}


REDIS_HOST = '192.168.108.20'   # redis Master機器的ip
REDIS_PORT = 
           

4、執行爬蟲

4.1 啟動Master機器的redis-server

# 啟動Master機器上的redis-server
redis-server /redis/redis-stable/redis.conf
           

4.2 執行爬蟲程式

将Scrapy-redis項目的爬蟲代碼,放到slaves機器上。進入項目的spider目錄,然後分别在slaves機器上啟動scrapy-redis項目爬蟲程式,不分先後。

4.3 Master機器的redis-cli

在Master機器的redis-cli裡啟動需要爬取的url。

5、将redis資料永久存儲

爬蟲程式爬取之後,我們将redis中的資料進行儲存到mongodb資料庫中。如下

import json
import redis
import pymongo


def main():
    # 指定Redis資料庫資訊
    rediscli = redis.StrictRedis(host='192.168.108.20', port=, db=)
    # 指定MongoDB資料庫資訊
    mongocli = pymongo.MongoClient(host='localhost', port=)

    # 建立資料庫名
    db = mongocli['redis58test']
    # 建立表名
    sheet = db['redis58_sheet2']

    while True:
        # FIFO模式為 blpop,LIFO模式為 brpop,擷取鍵值
        source, data = rediscli.blpop(["redis58spider_redis:items"])

        item = json.loads(data)
        sheet.insert(item)

        try:
            print(u"Processing: %(name)s <%(link)s>" % item)
        except KeyError:
            print(u"Error procesing: %r" % item)


if __name__ == '__main__':
    main()
           

資料如圖:

Scrapy架構學習(八)----Scrapy-redis分布式爬蟲學習