WebMagic實現分布式抓取以及斷點抓取

2021-08-31 16:16:31 字數 3269 閱讀 7151

從去年到今年,筆者主要負責的是與合作方的內容對接,新增的合作商不是很多的情況下,在我自從去年引入了 webmagic 這個爬蟲框架之後,基本很少需要去關注維護爬蟲,做的最多的是新接入合作商去寫對應爬蟲抓取模板。

因為在**中實現了增量抓取,單機也足以承擔日常的抓取工作。

在前兩周,由於公司拓展新的業務渠道,需要接入的合作商瞬間增加了 3 倍,又被要求在 2 天內全部接入,那兩天和另外乙個同事,幾乎都在忙著適配模板。

但鑑於當時需求緊,沒有時間對爬蟲部分**進行重構公升級,單機抓取也不行,而且會影響正常抓取任務的執行,於是臨時想了個辦法在其他伺服器上抓取某個合作商,才坎坷解決了這個問題,但這也並非長久之計。

因為剛剛引入 webmagic 這個框架的時候,還不是太熟悉,使用的 scheduler 是預設基於記憶體的佇列queuescheduler,當待抓取的 url 太多時,記憶體就被佔滿了,從而導致 oom。

如果要實現分布式抓取,前提需要使用基於 redis 的redisscheduler

在建立爬蟲的時候,手動設定 scheduler 為 redisscheduler。

1
spider.setscheduler(new redisscheduler(jedispool));
redisscheduler 需要傳入 jedispool 引數。

如果使用的是 springboot,可以宣告乙個 redisconfig 的配置類。

123

4567

891011

1213

1415

1617

1819

2021

2223

2425

2627

2829

30

@configuration

public class redisconfig ")

private string host;

@value("$")

private int port;

@value("$")

private string password;

@value("$")

private int timeout;

@value("$")

private int maxidle;

@value("$")

private long maxwaitmillis;

@bean

public jedispool redispoolfactory()

}

如果使用的是 spring,可以在 xml 中配置宣告乙個 bean 節點。

123

4567

891011

1213

宣告了 jedispool 之後,直接在**中注入即可。

1

2

@autowired

private jedispool jedispool;

僅僅配置了 redisscheduler,還無法達成我們的進行分布式抓取的目的,如果需要進行分布式抓取,其佇列應該是共享的,即多台伺服器的多個爬蟲使用同乙個 redis url 佇列,取 url 或者新增 url 都是同乙個。

又因為是 webmagic 在幫助我們管理 scheduler,所以 url 的維護也是 webmagic 在做。

先看一段 webmagic 的原始碼

123

4567

891011

1213

1415

1617

18

public void run()  started!",getuuid());

while (!thread.currentthread().isinterrupted() && stat.get() == stat_running)

// wait until new url added

waitnewurl();

} else

}// ......

}

可以看到 webmagic 抓取的時候通過這行**獲取佇列中待抓取的 url 位址。

1
request request = scheduler.poll(this);
而這個 this 是指實現了 task 介面的物件,即把當前的 spider 物件作為引數傳入。

因為我們使用了 redisscheduler,所以進入該類的 poll() 方法檢視。

1
string url = jedis.lpop(getqueuekey(task));
通過 task 的 uuid 獲取到佇列的 key,然後利用 redis 的 list 的 lpop 命令從佇列左側彈出乙個帶抓取的 url,構造 request 物件。

同樣的檢視 poll 上面的 pushwhennoduplicate 方法,是將待抓取請求的 url push到佇列的右側,而這個佇列也是通過 spider 的 uuid 裡唯一確定的。

1
jedis.rpush(getqueuekey(task), request.geturl());
所以,如果要實現分布式同時抓取同乙個佇列,就需要保持 多個 spider 的 uuid 是一致的

用過 webmagic 的人都知道,爬蟲啟動需要給他乙個起始 url,然後通過這個 url 獲取新的 url;所以如果需要進行分布式抓取,肯定爬蟲的起始 url 是不能相同的,因為webmagic 會對重複的 url 進行自動去重。

因此爬蟲的架構圖從

變成了如下架構

即保證多個爬蟲使用同乙個 redis 佇列。具體思路就是第一只通過起始 url 爬蟲啟動的時候,記錄啟動爬蟲的設定uuid,然後啟動其他爬蟲的時候,設定爬蟲的 uuid 為記錄的 uuid 的值。

**中體現的就是如下所示:

啟動其他爬蟲的時候,手動從佇列中獲取 url 設定為啟動 url 即可。

筆者實現的爬蟲啟動是通過定時任務啟動的,因為其他爬蟲與第一只爬蟲的入口不同,因此定義了兩個任務去排程,並且兩個任務之間有 30s 的間隔時間,防止第一只爬蟲還未新增 url 到佇列當中,而造成其他爬蟲無 url 可抓取情況的發生。

基於這個思路,因 url 放在 redis 之中,所以同時也可以實現斷點抓取。

webmagic 的原始碼很簡潔易懂,可以學習到很多東西,尤其是多執行緒以及鎖的應用,很值得借鑑學習。

colly 分布式抓取 6

根據抓取任務的需求,可以以不同的方式實現分布式抓取。大多數情況下,擴充套件網路通訊層就足夠了,使用 和colly的 切換器可以很容易地實現這一點 當http請求分布在多個 之間時,使用 切換器進行抓取仍然是集中的。colly通過其 setproxyfunc 成員支援 切換。任何自定義函式都可以通過f...

分布式事務 分布式事務的實現

如果在多個服務中需要對不同的資料庫進行操作。因為不同服務操作的資料庫都不同,所以保證在同乙個事務中完成操作顯然是不科學的。那實現分布式事務的思想 1 方法入口,建立一條日誌記錄,狀態定義為初始狀態,即儲存本條日誌記錄 可以儲存在資料庫中,也可以寫出到本地磁碟檔案 2 可以在非同步執行緒或在定時任務中...

分布式 分布式鎖

本質是利用redis的setnx 方法的特性來加鎖,setnx 即key不存在則設定key,否則直接返回false,要求在分布式系統中使用同乙個redis服務,以下提供兩種解決方案 1 直接使用redistemplate 這其實並不能完全保證高併發下的安全問題,因為可能在鎖過期之後該執行緒尚未執行完...