1. 程式人生 > 實用技巧 >Scrapy_redis爬蟲專案

Scrapy_redis爬蟲專案

Scrapy 和 scrapy-redis的區別

scrapy是一個Python爬蟲框架,爬取效率極高,具有高度定製性,但是不支援分散式。而scrapy-redis一套基於redis資料庫、執行在scrapy框架之上的元件,可以讓scrapy支援分散式策略,Slaver端共享Master端redis資料庫裡的item佇列、請求佇列和請求指紋集合。而為什麼選擇redis資料庫,是因為redis支援主從同步,而且資料都是快取在記憶體中的,所以基於redis的分散式爬蟲,對請求和資料的高頻讀取效率非常高。

pip install scrapy-redis

Scrapy-redis提供了下面四種元件(components):

Scheduler
Duplication Filter
Item Pipeline
Base Spider

Scheduler:

Scrapy改造了python本來的collection.deque(雙向佇列)形成了自己的Scrapy queue,但是Scrapy多個spider不能共享待爬取佇列Scrapy queue, 即Scrapy本身不支援爬蟲分散式,scrapy-redis 的解決是把這個Scrapy queue換成redis資料庫(也是指redis佇列),從同一個redis-server存放要爬取的request,便能讓多個spider去同一個資料庫裡讀取。

Scrapy中跟“待爬佇列”直接相關的就是排程器Scheduler,它負責對新的request進行入列操作(加入Scrapy queue),取出下一個要爬取的request(從Scrapy queue中取出)等操作。它把待爬佇列按照優先順序建立了一個字典結構,然後根據request中的優先順序,來決定該入哪個佇列,出列時則按優先順序較小的優先出列。為了管理這個比較高階的佇列字典,Scheduler需要提供一系列的方法。但是原來的Scheduler已經無法使用,所以使用Scrapy-redis的scheduler元件。

Duplication Filter:

Scrapy中用集合實現這個request去重功能,Scrapy中把已經發送的request指紋放入到一個集合中,把下一個request的指紋拿到集合中比對,如果該指紋存在於集合中,說明這個request傳送過了,如果沒有則繼續操作。

def request_seen(self, request):
        # self.request_figerprints就是一個指紋集合 

        fp = self.request_fingerprint(request)

        # 這就是判重的核心操作 

        if fp in self.fingerprints:

            return True

        self.fingerprints.add(fp)

        if self.file:

            self.file.write(fp + os.linesep)

在scrapy-redis中去重是由Duplication Filter元件來實現的,它通過redis的set 不重複的特性,巧妙的實現了Duplication Filter去重。scrapy-redis排程器從引擎接受request,將request的指紋存⼊redis的set檢查是否重複,並將不重複的request push寫⼊redis的 request queue。

引擎請求request(Spider發出的)時,排程器從redis的request queue佇列⾥里根據優先順序pop 出⼀個request 返回給引擎,引擎將此request發給spider處理。

Item Pipeline:

引擎將(Spider返回的)爬取到的Item給Item Pipeline,scrapy-redis 的Item Pipeline將爬取到的 Item 存⼊redis的 items queue。

修改過Item Pipeline可以很方便的根據 key 從 items queue 提取item,從⽽實現items processes叢集。

Base Spider:

不在使用scrapy原有的Spider類,重寫的RedisSpider繼承了Spider和RedisMixin這兩個類,RedisMixin是用來從redis讀取url的類。

當我們生成一個Spider繼承RedisSpider時,呼叫setup_redis函式,這個函式會去連線redis資料庫,然後會設定signals(訊號):

一個是當spider空閒時候的signal,會呼叫spider_idle函式,這個函式呼叫schedule_next_request函式,保證spider是一直活著的狀態,並且丟擲DontCloseSpider異常。
一個是當抓到一個item時的signal,會呼叫item_scraped函式,這個函式會呼叫schedule_next_request函式,獲取下一個request。
scrapy_reedis架構

Scrapy-Redis分散式策略:
假設有四臺伺服器:任意一臺電腦都可以作為 Master端 或 Slaver端,比如:

--Master端(核心伺服器) :使用 一臺伺服器當做Master,搭建一個Redis資料庫,不負責爬取,只負責url指紋判重、Request的分配,以及資料的儲存
--Slaver端(爬蟲程式執行端) :使用 三臺伺服器作為Slaver,負責執行爬蟲程式,執行過程中提交新的Request給Master

首先Slaver端從Master端拿任務(Request、url)進行資料抓取,Slaver抓取資料的同時,產生新任務的Request便提交給 Master 處理;
Master端只有一個Redis資料庫,負責將未處理的Request去重和任務分配,將處理後的Request加入待爬佇列,並且儲存爬取的資料。
1、修改settings.py檔案,最簡單的方式是使用redis替換機器記憶體,你只需要在 settings.py 最會面加上以下程式碼,就能讓你的爬蟲變為分散式。

# REDIS配置
# 去重類--指定那個去重方法給 request 物件去重
# DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter'
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 佇列--指定scheduler佇列,排程器記憶體的是待爬取連線和已爬取物件指紋
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
# 佇列內容是否持久化儲存--為False的時候,關閉redis的時候清空redis
SCHEDULER_PERSIST = True
REDIS_URL = 'redis://192.168.160.100:6379'

自定義爬蟲類繼承RedisSpider

class JDSpider(RedisSpider):
    name = "jdSpider"
    redis_key = "JDUrlsSpider"

 
    def parse(self, response):
        print(response.url)
        # 編寫爬蟲
        pass

    pass

可以寫一個單獨的爬蟲檔案,用來獲取爬取路由,如:

import redis
# 1.生成指定的路由,寫入到redis中
conn = redis.Redis(host="192.168.160.100", port=6379)
url = ''
conn.lpush("JDUrlsSpider", url)
  • scrapy_redis執行命令:
    寫入到一個py檔案執行這個py檔案就可以啟動爬蟲
from scrapy import cmdline

if __name__ == '__main__':
    cmdline.execute("scrapy crawl jdSpider".split())
    pass