python爬蟲框架——Scrapy架構原理介紹

說起寫爬蟲整陌,大多數第一時間想到的就是python了恃轩。python語法簡潔明了锁荔,加上及其豐富好用的庫蟀给,用它來寫爬蟲有天然的優(yōu)勢。

之前學python的時候也用requests+lxml寫過幾個爬蟲玩阳堕,但是都就爬取一些內容就沒繼續(xù)下去了跋理,都沒做成一個項目,中間python也荒廢了好久恬总。最近要學kafka前普,就打算爬點數據來實踐實踐。于是就學起scrapy來壹堰,總的來說拭卿,scrapy還是很容易上手的骡湖,也比較簡單,花了幾天的時間學習加實踐峻厚,也漸漸的掌握了這個爬蟲框架勺鸦。所以打算寫個博客做個總結,以免之后又太久沒用忘記了目木。

scrapy架構原理

scrapy架構圖
[圖片上傳失敗...(image-21611e-1532663400039)]

  1. scrapy引擎向spider獲取起始Request集合,也就是spider中定義的start_urls换途。如果spider重寫了start_requests()方法,那么這個方法返回的Request集合就是起始Request刽射。
  2. scrapy引擎將拿到的Request發(fā)給調度中心開始調度军拟。
  3. scrapy引擎向調度中心請求獲取下一個要爬取的Request。
  4. scrapy引擎拿到Request后誓禁,然后將Request發(fā)給下載器懈息。這個過程經過一系列在settings.py中配置的下載中間件,所有在settings.py中配置的下載中間件會依次對Request進行處理摹恰”杓蹋——對應DownloaderMiddleware#process_request()方法
  5. 下載器根據Request拉取響應的內容,比如Request的url是http://www.baidu.com俗慈,下載器就會拉取對應的網頁內容下來并封裝成Response對象姑宽。
  6. 下載器將Response發(fā)送給scrapy引擎。這個過程也會經過一系列在settings.py中配置的下載中間件闺阱,這些下載中間件會依次對Response進行處理炮车。——對應DownloaderMiddleware#process_response()方法
  7. scrapy引擎拿到Response后將Response發(fā)給spider,交給對應的spider函數處理酣溃。這里默認方法是parse(),這個回調方法構造Request的時候指定瘦穆。引擎發(fā)送Response的過程會經過一系列在settings.py中配置的spider中間件,這些spider中間件會依次對Response進行一些處理赊豌】富颍——對應SpiderMiddleware#process_spider_input()
  8. spider處理完Response后會返回一個result,這個result是一個包含 Request 或 Item 對象的可迭代對象(iterable)碘饼。然后將result發(fā)給scrapy引擎熙兔,這個過程也會經過一系列在settings.py中配置的spider中間件,這些spider中間件會依次對這個result進行一些處理派昧∏——對應SpiderMiddleware#process_spider_output()
  9. scrapy引擎拿到這個result后,會將其中的Item發(fā)送給Item Pipeline處理,這些item就會被一系列我們在settings.py中配置的pipeline處理蒂萎。同時秆吵,scrapy也會將result中的Request發(fā)給調度中間準備調度。
  10. 繼續(xù)重復第2步的步驟五慈,直到所有的Request全部處理完后程序退出纳寂。

在scrapy 0.15版本后主穗,spider中間件新增了一個方法,用于處理第一步中spider發(fā)送給引擎的Request,也就是SpiderMiddleware#process_start_requests(start_requests, spider)

scrapy的組件介紹

一毙芜、Spider

Spider組件主要用來生成要爬取的url忽媒,解析返回的內容,然后生成新的url繼續(xù)交給scrapy去爬取腋粥,或者生成item交給pipeline處理晦雨。

編寫scrapy爬蟲應用時,我們大多數精力應該都是放在spider的編寫上面隘冲。

  1. 通過定義start_urls參數或者重寫start_requests()方法來給scrapy引擎提供起始的拉取url闹瞧。
  2. 之后,scrapy引擎拿到url后去獲取對應url網頁中的內容后就會回調spider中的parse()方法展辞,我們可以重寫parse()方法來解析scrapy引擎返回回來的內容奥邮。
  3. parse()方法中,我們可以返回一個可迭代的對象罗珍,可以理解為是一個list洽腺,因為list也是一個可迭代對象,這個list里面可以放Request對象或者Item對象覆旱。
  4. scrapy拿到list后蘸朋,會遍歷整個容器,然后把Request再放進調度等會去獲取內容通殃,對于Item對象度液,scrapy引擎就把這個item對象發(fā)到pipeline去處理厕宗。在pipeline有用戶自己編寫的一套處理邏輯画舌,可以選擇的將item存到文件或者數據庫中。
# -*- coding: utf-8 -*-  
import scrapy  
  
class TestSpider(scrapy.Spider):  
    name = "kongtrio"  
    # 定義爬取的域名已慢,如果返回的url所屬的域名不在這個列表里面曲聂,scrapy就不會去爬取內容
    allowed_domains = ["bbs.hupu.com"]  
    # 定義起始的url
    start_urls = (  
        'http://bbs.hupu.com/bxj/',  
    )  
    # 如果重寫了這個方法,scrapy引擎取到的起始url就是這個方法返回的內容了
    # 這樣start_urls就不會生效了
    def start_requests(self):
        for i in range(1, 10):
            yield scrapy.Request('http://bbs.hupu.com/bxj-' + str(i))
                  
    def parse(self, response):  
        # scrapy拉取到url的內容后佑惠,會封裝成Response對象,然后回調這個parse()方法
        # 我們可以對這個response進行解析朋腋,然后根據策略返回響應的內容
        # scrapy 自帶了xpath的方式解析內容,xpath教程可以看這篇  https://blog.csdn.net/u013332124/article/details/80621638
        title_href = response.xpath(".//a[@class='title']/@href").extract_first()
        title = response.xpath(".//a[@class='title']/text()").extract_first()
        # 返回一個request對象和一個item對象膜楷,request對象放的是標題的url旭咽,后面scrapy會繼續(xù)讀取這個url然后交給parse繼續(xù)解析
        return [scrapy.Request(content_url, self.post_content_parse, dont_filter=True),{"title":title}]

二、pipeline

這也是我們需要關心的組件赌厅。前面spider返回的item會經過scrapy引擎的調度發(fā)向pipeline穷绵。
pipeline的組件做的事情很簡單,就是拿到item特愿,然后具體的操作用戶自己實現(xiàn)仲墨。


class HupuSpiderPipeline(object):
    def process_item(self, item, spider):
        if not item["title"]:
            # 如果這個pipeline拋出DropItem異常勾缭,那么這個item就不會傳給后面的pipeline了
            raise DropItem("invalid item")
        title = item["title"]
        print(title)
        # return后 會把這個item繼續(xù)傳給后面的pipeline
        return item

上面這個pipeline做的事情很簡單,就是從item中獲取title目养,然后打印出來俩由。

我們可以寫多個pipeline,分別做不同業(yè)務的事情癌蚁。但是要注意的是幻梯,在process_item()方法中,必須將item返回努释,不然后面的pipeline就不會被調起來處理item了礼旅。或者拋出DropItem異常也會中斷item的傳遞洽洁。

編寫好pipeline之后還要記得在settings.py里面配置痘系,這樣pipeline才會真正被scrapy引擎知道,并開始工作饿自。

# 后面的數字表示pipeline的次序
ITEM_PIPELINES = {
    'hupu_spider.pipelines.HupuSpiderPipeline': 300,
    # 'hupu_spider.pipelines.HupuImgDownloadPipeline': 400,
}

編寫完pipeline和spider汰翠,我們其實就基本實現(xiàn)了一個簡單的scrapy爬蟲應用了。挺大一部分場景也只要我們編寫pipeline和spider就可以了昭雌。當然复唤,其他的組件也需要了解一下,以面對豐富多樣的需求變動烛卧。

三佛纫、下載中間件

下載中間件主要是用于在scrapy引擎發(fā)送Request到下載器和下載器返回Response給scrapy引擎的過程中。

  1. scrapy引擎發(fā)送Request到下載器
    scrapy引擎發(fā)送Request到下載器的過程中总放,會經過一個個的下載中間件呈宇,這些中間件會對Request進行處理,可以將處理后的Request再發(fā)送給下一個中間件局雄,也可以中斷Request的處理甥啄,甚至可以不需要經過下載器就直接生成Response然后返回給scrapy引擎,具體的策略由代碼實現(xiàn)來決定炬搭。在所有的中間件都處理過后蜈漓,下載器拿到Request就會開始下載內容然后返回Response了。
  2. 下載器返回Response給scrapy引擎
    下載器通過Request拉取到數據后宫盔,就會封裝成Response返回給scrapy引擎融虽,在這個過程中,也會經過這些下載中間件的處理灼芭。下載中間件可以生成Request重新交給scrapy引擎處理有额,也可以對Response進行一些處理后交給下一個下載中間件,最后抵達scrapy引擎。
下載中間件的定義和使用

我們需要寫一個類來繼承DownloaderMiddleware谆吴,并在settings.py中配置編寫好的這個下載中間件倒源。

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.CustomDownloaderMiddleware': 543,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
}
  • 后面的數字表示該下載中間件的順序,數字越小越接近scrapy引擎
  • 如果要禁用內置的一些下載中間件句狼,可以將數字設置為None

編寫下載中間件需要關注的幾個方法:
process_request(request, spider): 這個方法接收scrapy傳過來的Request對象笋熬,我們可以在這個方法里面對這個Request進行一些處理,然后根據返回的對象做一些操作:

  1. 返回None腻菇,通知下一個下載中間件繼續(xù)對這個Request進行處理
  2. 返回Response對象胳螟,直接生成Response發(fā)給scrapy引擎,這樣Request就不會交給下載器去下載內容了筹吐。注意糖耸,生成的Response還是會經過一個個下載中間件處理
  3. 返回Request對象,直接把新的Request返回給scrapy引擎重新調度丘薛,但是后面的下載中間件就不會再執(zhí)行了
  4. 如果其raise一個 IgnoreRequest 異常嘉竟,則安裝的下載中間件的 process_exception() 方法會被調用

process_response(request, response, spider):這個方法接收下載器或者其他下載中間件發(fā)送過來的Response,同時還包括對應的Request對象洋侨,并在方法內對response進行一些處理舍扰,然后根據返回的對象類型做一些操作:

  1. 如果返回一個Request對象,下載中間件的執(zhí)行就會被停止希坚,并且會把這個Request對象交給scrapy引擎重新調度边苹。
  2. 如果返回一個Response對象,就會通知下一個下載中間件繼續(xù)對這個response進行處理
  3. 如果其拋出一個 IgnoreRequest 異常裁僧,則調用request的errback(Request.errback)

總結
理解了scrapy的整個結構后个束,下載中間件的功能還是比較好理解的。官方目前也內置了很多實用的下載中間件聊疲,所以在大多數場景下茬底,也不用我們手動去編寫下載中間件。不過復雜的場景還是用的上的售睹,多學一點也沒有壞處桩警。

四、Spider中間件

Spider中間件主要用于scrapy引擎和spider之間的數據處理昌妹。包括spider發(fā)送Request和Item給scrapy引擎以及scrapy引擎發(fā)送Response給spider。
1. spider發(fā)送Request和Item給scrapy引擎
spider返回Request和Item給scrapy引擎過程中握截,會經過一個個spider中間件對result進行處理飞崖。spider中間件可以拿到spider返回的result和請求返回的response,這個result是Request和Item的迭代對象谨胞。spider中間件進行一些處理之后返回一個result固歪。然后下一個spider中間件繼續(xù)拿到result處理。
2.scrapy引擎發(fā)送Response給spider
scrapy引擎發(fā)送Response給spider的過程中,會經過一個個spider中間件對Response進行處理牢裳。處理之后spider中間件可以返回None或者拋出一個異常逢防。返回None的話就會繼續(xù)調用下一個spider中間件繼續(xù)處理response,拋出異常的話就不會往下執(zhí)行了蒲讯。

spider中間件的定義和使用

我們需要寫一個類來繼承SpiderMiddleware忘朝,并在settings.py中配置編寫好的這個下載中間件。

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
    'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware': None,
}
  • 后面的數字表示該下載中間件的順序判帮,數字越小越接近scrapy引擎
  • 如果要禁用內置的一些下載中間件局嘁,可以將數字設置為None

編寫spider中間件需要關注的幾個方法:
process_spider_input(response, spider):
接收scrapy引擎?zhèn)鬟^來的response,用戶可以在方法內處理該response晦墙。根據返回類型的不同會有不同的表現(xiàn)行為:

  1. 如果其返回 None 悦昵,Scrapy將會繼續(xù)處理該response,調用所有其他的中間件直到spider處理該response
  2. 如果其跑出一個異常(exception)晌畅,Scrapy將不會調用任何其他中間件的 process_spider_input() 方法但指,并調用request的errback。errback的輸出將會以另一個方向被重新輸入到中間件鏈中抗楔,使用 process_spider_output() 方法來處理枚赡,當其拋出異常時則帶調用 process_spider_exception() 。
    process_spider_output(response, result, spider):
    當Spider處理response返回result時谓谦,該方法被調用贫橙。
    必須返回返回包含 Request 或 Item 對象的可迭代對象。
    process_spider_exception(response, exception, spider):
    當spider或(其他spider中間件的) process_spider_input() 拋出異常時反粥, 該方法被調用
  3. 如果其返回 None 卢肃,Scrapy將繼續(xù)處理該異常,調用中間件鏈中的其他中間件的 process_spider_exception() 方法才顿,直到所有中間件都被調用莫湘,該異常到達引擎(異常將被記錄并被忽略)
  4. 如果其返回一個可迭代對象,則中間件鏈的 process_spider_output() 方法被調用郑气, 其他的 process_spider_exception() 將不會被調用
    process_start_requests(start_requests, spider):
    0.15 新版功能
    該方法以spider 啟動的request為參數被調用幅垮,執(zhí)行的過程類似于 process_spider_output() ,只不過其沒有相關聯(lián)的response并且必須返回request(不是item)尾组。
    其接受一個可迭代的對象(start_requests 參數)且必須返回另一個包含 Request 對象的可迭代對象

總結
目前scrapy也內置了很多spider中間件忙芒,可以滿足大多數場景。雖然平常時候我們可能不會有寫spider中間件的時候讳侨,但是還是有必要了解的呵萨。

五、總結

scrapy的架構原理以及相關組件介紹差不多到這里就結束了跨跨。熟悉Python的話潮峦,scrapy學起來還是很快的。有空寫寫爬蟲也是挺有意思的,大家有空可以學一學忱嘹。

想深入學習還可以多去官方的scrapy中文文檔看一看:
http://docs.pythontab.com/scrapy/scrapy0.24/intro/overview.html

最后嘱腥,有對爬蟲或者java技術感興趣的歡迎聯(lián)系我一起交流~本人郵箱在下方

我的CSDN博客地址:
https://blog.csdn.net/u013332124/article/details/80645690

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拘悦,隨后出現(xiàn)的幾起案子齿兔,更是在濱河造成了極大的恐慌,老刑警劉巖窄做,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愧驱,死亡現(xiàn)場離奇詭異,居然都是意外死亡椭盏,警方通過查閱死者的電腦和手機组砚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掏颊,“玉大人糟红,你說我怎么就攤上這事∥谝叮” “怎么了盆偿?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長准浴。 經常有香客問我事扭,道長,這世上最難降的妖魔是什么乐横? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任求橄,我火速辦了婚禮,結果婚禮上葡公,老公的妹妹穿的比我還像新娘罐农。我一直安慰自己,他們只是感情好催什,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布涵亏。 她就那樣靜靜地躺著,像睡著了一般蒲凶。 火紅的嫁衣襯著肌膚如雪气筋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天豹爹,我揣著相機與錄音裆悄,去河邊找鬼。 笑死臂聋,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播孩等,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼艾君,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肄方?” 一聲冷哼從身側響起冰垄,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎权她,沒想到半個月后虹茶,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡隅要,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年蝴罪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片步清。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡要门,死狀恐怖,靈堂內的尸體忽然破棺而出廓啊,到底是詐尸還是另有隱情欢搜,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布谴轮,位于F島的核電站炒瘟,受9級特大地震影響,放射性物質發(fā)生泄漏第步。R本人自食惡果不足惜疮装,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雌续。 院中可真熱鬧斩个,春花似錦、人聲如沸驯杜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸽心。三九已至滚局,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間顽频,已是汗流浹背藤肢。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留糯景,地道東北人嘁圈。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓省骂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親最住。 傳聞我的和親對象是個殘疾皇子钞澳,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容