1. 程式人生 > >scrapy-redis分散式爬蟲實戰

scrapy-redis分散式爬蟲實戰

Scrapy-Redis程式碼實戰

Scrapy 是一個通用的爬蟲框架,但是不支援分散式,Scrapy-redis是為了更方便地實現Scrapy分散式爬取,而提供了一些以redis為基礎的元件(僅有元件)。

scrapy-redis在scrapy的架構上增加了redis,基於redis的特性拓展瞭如下四種元件:

  • Scheduler
  • Duplication Filter
  • Item Pipeline
  • Base Spider

    scrapy-redis架構

Scheduler

Scrapy原本的queue是不支援多個spider共享一個佇列的,scrapy-redis通過將queue改為redis實現佇列共享。

Duplication Filter

Scrapy中通過Python中的集合實現request指紋去重,在scrapy-redis中去重是由Duplication Filter元件來實現的,它通過redis的set不重複的特性,巧妙的實現了DuplicationFilter去重。

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-Redis

python3.6 -m pip install scrapy-redis

專案練習

首先修改配置檔案

BOT_NAME = 'cnblogs'
SPIDER_MODULES = ['cnblogs.spiders']
NEWSPIDER_MODULE = 'cnblogs.spiders'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
DOWNLOAD_DELAY = 2 # 等待2s
MY_USER_AGENT = ["Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36",
    "Mozilla/5.0+(Windows+NT+5.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/28.0.1500.95+Safari/537.36+SE+2.X+MetaSr+1.0",
    "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/50.0.2657.3+Safari/537.36"]
# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
   'cnblogs.middlewares.UserAgentMiddleware': 543,
}
LOG_LEVEL = "ERROR"

ITEM_PIPELINES = {
   'cnblogs.pipelines.MongoPipeline': 300,
}
#將結果儲存到Mongo資料庫
MONGO_HOST = "127.0.0.1"  # 主機IP
MONGO_PORT = 27017  # 埠號
MONGO_DB = "spider_data"  # 庫名
MONGO_COLL = "cnblogs_title"  # collection名

#需要將排程器的類和去重的類替換為 Scrapy-Redis 提供的類
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 7001 #Redis叢集中其中一個節點的埠

#配置持久化
#Scrapy-Redis 預設會在爬取全部完成後清空爬取佇列和去重指紋集合。
#SCHEDULER_PERSIST = True

#設定重爬
#SCHEDULER_FLUSH_ON_START = True

程式碼要改的地方有兩處:
第一處是繼承的RedisSpider
第二處就是start_urls改為了redis_key。

# -*- coding: utf-8 -*-
import scrapy
import datetime
from scrapy_redis.spiders import RedisSpider
class CnblogSpider(RedisSpider):
    name = 'cnblog'
    redis_key = "myspider:start_urls"
    #start_urls = [f'https://www.cnblogs.com/c-x-a/default.html?page={i}' for i in range(1,2)]
    
    def parse(self, response):
        main_info_list_node = response.xpath('//div[@class="forFlow"]')
        content_list_node = main_info_list_node.xpath(".//a[@class='postTitle2']/text()").extract()
        for item in content_list_node:
            url = response.url
            title=item
            crawl_date = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            item = {}
            item['url'] = url
            item['title'] = title.strip() if title else title
            item['crawl_date'] = crawl_date
            yield item

因為Scrapy-Redis是以Redis為佇列進行訊息共享的,所以我們的任務需要提前插入到資料庫,它的key就叫我們指定的"myspider:start_urls"。
在之前建立好的redis叢集中插入任務,首先使用叢集的模式連線資料庫

redis-cli -c -p 7000 #我的redis叢集的一個Master節點埠

執行下面的語句插入任務

lpush myspider:start_urls https://www.cnblogs.com/c-x-a/default.html?page=1
lpush myspider:start_urls https://www.cnblogs.com/c-x-a/default.html?page=2

然後檢視

lrange myspider:start_urls 0 10

看到我們的任務,好了任務插入成功了。
接下來就是執行程式碼了,執行完程式碼之後,去檢視三處。
第一處,檢視redis的任務發現任務已經沒有了

(empty list or set)

第二處,檢視mongo資料庫,發現我們成功儲存了結果。

第三處,你會發現的你爬蟲程式並沒有結束,這個其實是正常的,因為我們使用了scrapy-redis之後,爬蟲程式會一直取redis中的任務,如果沒有任務了就等待,如果在redis插入了新的任務他就會繼續進行爬蟲程式,之後又進入等待任務的狀態。

關注公眾號:Python學習開發,後臺回覆:redis即可獲取原始碼。

參考資料

https://segmentfault.com/a/1190000014333162?utm_source=channel-hott