如何構建一個分布式爬蟲:理論篇

前言

本系列文章計劃分三個章節(jié)進行講述钩述,分別是理論篇项戴、基礎篇和實戰(zhàn)篇彩库。理論篇主要為構建分布式爬蟲而儲備的理論知識肤无,基礎篇會基于理論篇的知識寫一個簡易的分布式爬蟲,實戰(zhàn)篇則會以微博為例骇钦,教大家做一個比較完整且足夠健壯的分布式微博爬蟲宛渐。通過這三篇文章,希望大家能掌握如何構建一個分布式爬蟲的方法眯搭;能舉一反三皇忿,將celery用于除爬蟲外的其它場景。目前基本上的博客都是教大家使用scrapyd或者scrapy-redis構建分布式爬蟲坦仍,本系列文章會從另外一個角度講述如何用requests+celery構建一個健壯的鳍烁、可伸縮并且可擴展的分布式爬蟲。

本系列文章屬于爬蟲進階文章繁扎,期望受眾是具有一定Python基礎知識和編程能力幔荒、有爬蟲經驗并且希望提升自己的同學糊闽。小白要是感興趣,也可以看看爹梁,看不懂的話右犹,可以等有了一定基礎和經驗后回過頭來再看。

另外一點說明姚垃,本系列文章不是旨在構建一個分布式爬蟲框架或者分布式任務調度框架念链,而是利用現(xiàn)有的分布式任務調度工具來實現(xiàn)分布式爬蟲,所以請輕噴积糯。

分布式爬蟲概覽

  1. 何謂分布式爬蟲掂墓?
    通俗的講,分布式爬蟲就是多臺機器多個 spider 對多個 url 的同時處理問題看成,分布式的方式可以極大提高程序的抓取效率君编。

  2. 構建分布式爬蟲通暢需要考慮的問題
    (1)如何能保證多臺機器同時抓取同一個URL?
    (2)如果某個節(jié)點掛掉川慌,會不會影響其它節(jié)點吃嘿,任務如何繼續(xù)?
    (3)既然是分布式梦重,如何保證架構的可伸縮性和可擴展性兑燥?不同優(yōu)先級的抓取任務如何進行資源分配和調度?

基于上述問題琴拧,我選擇使用celery作為分布式任務調度工具贪嫂,是分布式爬蟲中任務和資源調度的核心模塊。它會把所有任務都通過消息隊列發(fā)送給各個分布式節(jié)點進行執(zhí)行艾蓝,所以可以很好的保證url不會被重復抓取斗塘;它在檢測到worker掛掉的情況下赢织,會嘗試向其他的worker重新發(fā)送這個任務信息,這樣第二個問題也可以得到解決馍盟;celery自帶任務路由于置,我們可以根據實際情況在不同的節(jié)點上運行不同的抓取任務(在實戰(zhàn)篇我會講到)。本文主要就是帶大家了解一下celery的方方面面(有celery相關經驗的同學和大耪炅耄可以直接跳過了)

Celery知識儲備

celery基礎講解

按celery官網的介紹來說

Celery 是一個簡單八毯、靈活且可靠的,處理大量消息的分布式系統(tǒng)瞄桨,并且提供維護這樣一個系統(tǒng)的必需工具话速。它是一個專注于實時處理的任務隊列,同時也支持任務調度芯侥。

下面幾個關于celery的核心知識點

  1. broker:翻譯過來叫做中間人泊交。它是一個消息傳輸?shù)闹虚g件乳讥,可以理解為一個郵箱。每當應用程序調用celery的異步任務的時候廓俭,會向broker傳遞消息云石,而后celery的worker將會取到消息,執(zhí)行相應程序研乒。這其實就是消費者和生產者之間的橋梁汹忠。
  2. backend: 通常程序發(fā)送的消息,發(fā)完就完了雹熬,可能都不知道對方時候接受了宽菜。為此,celery實現(xiàn)了一個backend橄唬,用于存儲這些消息以及celery執(zhí)行的一些消息和結果赋焕。
  3. worker: Celery類的實例,作用就是執(zhí)行各種任務仰楚。注意在celery3.1.25后windows是不支持celery worker的隆判!
  4. producer: 發(fā)送任務,將其傳遞給broker
  5. beat: celery實現(xiàn)的定時任務僧界∏揉郑可以將其理解為一個producer,因為它也是通過網絡調用定時將任務發(fā)送給worker執(zhí)行捂襟。注意在windows上celery是不支持定時任務的咬腕!

下面是關于celery的架構示意圖,結合上面文字的話應該會更好理解

celery架構示意圖

由于celery只是任務隊列葬荷,而不是真正意義上的消息隊列涨共,它自身不具有存儲數(shù)據的功能,所以broker和backend需要通過第三方工具來存儲信息宠漩,celery官方推薦的是 RabbitMQRedis举反,另外mongodb等也可以作為broker或者backend,可能不會很穩(wěn)定扒吁,我們這里選擇Redis作為broker兼backend火鼻。

關于redis的安裝和配置可以查看這里

實際例子

先安裝celery

pip install celery

我們以官網給出的例子來做說明,并對其進行擴展雕崩。首先在項目根目錄下魁索,這里我新建一個項目叫做celerystudy,然后切換到該項目目錄下盼铁,新建文件tasks.py粗蔚,然后在其中輸入下面代碼

from celery import Celery

app = Celery('tasks', broker='redis://''@223.129.0.190:6379/2', backend='redis://''@223.129.0.190:6379/3')

@app.task
def add(x, y):
    return x + y

這里我詳細講一下代碼:我們先通過app=Celery()來實例化一個celery對象,在這個過程中饶火,我們指定了它的broker支鸡,是redis的db 2,也指定了它的backend,是redis的db3, broker和backend的連接形式大概是這樣

redis://:password@hostname:port/db_number

然后定義了一個add函數(shù)冬念,重點是@app.task,它的作用在我看來就是add()
注冊為一個類似服務的東西
牧挣,本來只能通過本地調用的函數(shù)被它裝飾后急前,就可以通過網絡來調用。這個tasks.py中的app就是一個worker瀑构。它可以有很多任務裆针,比如這里的任務函數(shù)add。我們再通過在命令行切換到項目根目錄寺晌,執(zhí)行

celery -A tasks worker -l info

啟動成功后就是下圖所示的樣子

celery的worker啟動成功

這里我說一下各個參數(shù)的意思世吨,-A指定的是app(即Celery實例)所在的文件模塊,我們的app是放在tasks.py中呻征,所以這里是 tasks耘婚;worker表示當前以worker的方式運行,難道還有別的方式陆赋?對的沐祷,比如運行定時任務就不用指定worker這個關鍵字; -l info表示該worker節(jié)點的日志等級是info,更多關于啟動worker的參數(shù)(比如-c攒岛、-Q等常用的)請使用

celery worker --help

進行查看

將worker啟動起來后赖临,我們就可以通過網絡來調用add函數(shù)了。我們在后面的分布式爬蟲構建中也是采用這種方式分發(fā)和消費url的灾锯。在命令行先切換到項目根目錄兢榨,然后打開python交互端

from tasks import add
rs = add.delay(2, 2) # 這里的add.delay就是通過網絡調用將任務發(fā)送給add所在的worker執(zhí)行

這個時候我們可以在worker的界面看到接收的任務和計算的結果。

[2017-05-19 14:22:43,038: INFO/MainProcess] Received task: tasks.add[c0dfcd0b-d05f-4285-b944-0a8aba3e7e61] # worker接收的任務

[2017-05-19 14:22:43,065: INFO/MainProcess] Task tasks.add[c0dfcd0b-d05f-4285-b944-0a8aba3e7e61] succeeded in 0.025274309000451467s: 4 # 執(zhí)行結果

這里是異步調用顺饮,如果我們需要返回的結果吵聪,那么要等rsready狀態(tài)true才行。這里add看不出效果兼雄,不過試想一下吟逝,如果我們是調用的比較占時間的io任務,那么異步任務就比較有價值了

rs # <AsyncResult: c0dfcd0b-d05f-4285-b944-0a8aba3e7e61>

rs.ready() # true 表示已經返回結果了

rs.status # 'SUCCESS' 任務執(zhí)行狀態(tài)君旦,失敗還是成功

rs.successful() # True 表示執(zhí)行成功

rs.result # 4 返回的結果

rs.get() # 4 返回的結果

<celery.backends.redis.RedisBackend object at 0x30033ec> #這里我們backend 結果存儲在redis里

上面講的是從Python交互終端中調用add函數(shù),如果我們要從另外一個py文件調用呢嘲碱?除了通過import然后add.delay()這種方式金砍,我們還可以通過send_task()這種方式,我們在項目根目錄另外新建一個py文件叫做 excute_tasks.py麦锯,在其中寫下如下的代碼

from tasks import add

if __name__ == '__main__':
    add.delay(5, 10)

這時候可以在celery的worker界面看到執(zhí)行的結果

[2017-05-19 14:25:48,039: INFO/MainProcess] Received task: tasks.add[f5ed0d5e-a337-45a2-a6b3-38a58efd9760]
[2017-05-19 14:25:48,074: INFO/MainProcess] Task tasks.add[f5ed0d5e-a337-45a2-a6b3-38a58efd9760] succeeded in 0.03369094600020617s: 15

此外恕稠,我們還可以通過send_task()來調用,將excute_tasks.py改成這樣

from tasks import app
if __name__ == '__main__':
    app.send_task('tasks.add', args=(10, 15),)

這種方式也是可以的扶欣。send_task()還可能接收到為注冊(即通過@app.task裝飾)的任務鹅巍,這個時候worker會忽略這個消息

[2017-05-19 14:34:15,352: ERROR/MainProcess] Received unregistered task of type 'tasks.adds'.
The message has been ignored and discarded.

定時任務

上面部分講了怎么啟動worker和調用worker的相關函數(shù)千扶,這里再講一下celery的定時任務。

爬蟲由于其特殊性骆捧,可能需要定時做增量抓取澎羞,也可能需要定時做模擬登陸,以防止cookie過期敛苇,而celery恰恰就實現(xiàn)了定時任務的功能妆绞。在上述基礎上,我們將tasks.py文件改成如下內容

from celery import Celery
app = Celery('add_tasks', broker='redis://223.129.0.190:6379/2', backend='redis://223.129.0.190:6379/3')
app.conf.update(
   #  配置所在時區(qū) 
    CELERY_TIMEZONE='Asia/Shanghai',
    CELERY_ENABLE_UTC=True,
    #  官網推薦消息序列化方式為json 
    CELERY_ACCEPT_CONTENT=['json'],
    CELERY_TASK_SERIALIZER='json',
    CELERY_RESULT_SERIALIZER='json',
   # 配置定時任務
    CELERYBEAT_SCHEDULE={
        'my_task': {
            'task': 'tasks.add',  # tasks.py模塊下的add方法
            'schedule': 60,      # 每隔60運行一次
            'args': (23, 12),
        }
    }
)
@app.task
def add(x, y):
    return x + y

然后先通過ctrl+c停掉前一個worker枫攀,因為我們代碼改了括饶,需要重啟worker才會生效。我們再次以celery -A tasks worker -l info這個命令開啟worker来涨。

這個時候我們只是開啟了worker图焰,如果要讓worker執(zhí)行任務,那么還需要通過beat給它定時發(fā)送蹦掐,我們再開一個命令行技羔,切換到項目根目錄,通過

celery beat -A tasks -l info

celery beat v3.1.25 (Cipater) is starting.
__    -    ... __   -        _
Configuration ->
    . broker -> redis://223.129.0.190:6379/2
    . loader -> celery.loaders.app.AppLoader
    . scheduler -> celery.beat.PersistentScheduler
    . db -> celerybeat-schedule
    . logfile -> [stderr]@%INFO
    . maxinterval -> now (0s)
[2017-05-19 15:56:57,125: INFO/MainProcess] beat: Starting...

這樣就表示定時任務已經開始運行了笤闯。

眼尖的同學可能看到我這里celery的版本是3.1.25堕阔,這是因為celery支持的windows最高版本是3.1.25。由于我的分布式微博爬蟲的worker也同時部署在了windows上颗味,所以我選擇了使用 3.1.25超陆。如果全是linux系統(tǒng),建議使用celery4浦马。

此外时呀,還有一點需要注意,在celery4后晶默,定時任務(通過schedule調度的會這樣谨娜,通過crontab調度的會馬上執(zhí)行)會在當前時間再過定時間隔執(zhí)行第一次任務,比如我這里設置的是60秒的間隔磺陡,那么第一次執(zhí)行add會在我們通過celery beat -A tasks -l info啟動定時任務后60秒才執(zhí)行趴梢;celery3.1.25則會馬上執(zhí)行該任務。

關于定時任務更詳細的請看官方文檔celery定時任務


至此币他,我們把構建一個分布式爬蟲的理論知識都講了一遍坞靶,主要就是對于celery的了解和使用,這里并未涉及到celery的一些高級特性蝴悉,實戰(zhàn)篇可能會講解一些我自己使用的特性彰阴。

下一篇我將介紹如何使用celery寫一個簡單的分布式爬蟲,希望大家能有所收獲拍冠。


此外尿这,打一個廣告簇抵,我寫了一個分布式的微博爬蟲,主要就是利用celery做的分布式實戰(zhàn)篇我也將會以該項目其中一個模塊進行講解射众,有興趣的可以點擊看看碟摆,也歡迎有需求的朋友試用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末责球,一起剝皮案震驚了整個濱河市焦履,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雏逾,老刑警劉巖嘉裤,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異栖博,居然都是意外死亡屑宠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門仇让,熙熙樓的掌柜王于貴愁眉苦臉地迎上來典奉,“玉大人,你說我怎么就攤上這事丧叽∥谰粒” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵踊淳,是天一觀的道長假瞬。 經常有香客問我,道長迂尝,這世上最難降的妖魔是什么脱茉? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮垄开,結果婚禮上琴许,老公的妹妹穿的比我還像新娘。我一直安慰自己溉躲,他們只是感情好榜田,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锻梳,像睡著了一般箭券。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唱蒸,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天邦鲫,我揣著相機與錄音灸叼,去河邊找鬼神汹。 笑死庆捺,一個胖子當著我的面吹牛,可吹牛的內容都是我干的屁魏。 我是一名探鬼主播滔以,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼氓拼!你這毒婦竟也來了你画?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤桃漾,失蹤者是張志新(化名)和其女友劉穎坏匪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撬统,經...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡适滓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了恋追。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凭迹。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖苦囱,靈堂內的尸體忽然破棺而出嗅绸,到底是詐尸還是另有隱情,我是刑警寧澤撕彤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布鱼鸠,位于F島的核電站喉刘,受9級特大地震影響,放射性物質發(fā)生泄漏睦裳。R本人自食惡果不足惜造锅,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一廉邑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛛蒙,春花似錦糙箍、人聲如沸牵祟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雹拄。三九已至,卻和暖如春掌呜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背质蕉。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工势篡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人模暗。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓禁悠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兑宇。 傳聞我的和親對象是個殘疾皇子绷蹲,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內容