Python爬蟲Scrapy(九)_Spider中間件

本章將介紹scrapy框架里面的spider中間件规肴,更多內(nèi)容請(qǐng)參考: >本章將介紹Request與Response,更多內(nèi)容請(qǐng)參考:Python學(xué)習(xí)指南

Scrapy數(shù)據(jù)流

scrapy框架數(shù)據(jù)流

Scrapy中的數(shù)據(jù)流由執(zhí)行引擎控制缺猛,其過(guò)程如下:

  1. 引擎從Spiders中獲取到的最初的要爬取的請(qǐng)求(Requests)珍特。
  2. 引擎安排請(qǐng)求(Requests)到調(diào)度器中,并向調(diào)度器請(qǐng)求下一個(gè)要爬取的請(qǐng)求(Requests)晓褪。
  3. 調(diào)度器返回下一個(gè)要爬取的請(qǐng)求(Request)給請(qǐng)求堵漱。
  4. 引擎從上步中得到的請(qǐng)求(Requests)通過(guò)下載器中間件(Downloader Middlewares)發(fā)送給下載器(Downloader),這個(gè)過(guò)程中下載器中間件(Downloader Middlerwares)中的process_request()函數(shù)就會(huì)被調(diào)用。
  5. 一旦頁(yè)面下載完畢涣仿,下載器生成一個(gè)該頁(yè)面的Response勤庐,并將其通過(guò)下載中間件(Downloader Middlewares)中的process_response()函數(shù),最后返回給引擎
  6. 引擎從下載器中得到上步中的Response并通過(guò)Spider中間件(Spider Middewares)發(fā)送給Spider處理好港,這個(gè)過(guò)程中Spider中間件(Spider Middlewares)中的process_spider_input()函數(shù)會(huì)被調(diào)用到愉镰。
  7. Spider處理Response并通過(guò)Spider中間件(Spider Middlewares)返回爬取到的Item及(跟進(jìn)的)新的Request給引擎,這個(gè)過(guò)程中Spider中間件(Spider Middlewares)的process_spider_output()函數(shù)會(huì)被調(diào)用到钧汹。
  8. 引擎將上步中Spider處理的及其爬取到的Item給Item管道(Piplline),將Spider處理的Requests發(fā)送給調(diào)度器丈探,并向調(diào)度器請(qǐng)求可能存在的下一個(gè)要爬取的請(qǐng)求(Requests)
  9. (從第二步)重復(fù)知道調(diào)度器中沒(méi)有更多的請(qǐng)求(Requests)。

Spider中間件(Spider Middlewares)

Spider中間件是介入到Scrapy中的spider處理機(jī)制的鉤子框架拔莱,可以插入自定義功能來(lái)處理發(fā)送給Spiders的response,以及spider產(chǎn)生的item和request碗降。

1.激活Spider中間件(Spider Middlewares)

要啟用Spider中間件(Spider Middlewares),可以將其加入到SPIDER_MIDDLEWARES設(shè)置中塘秦。該設(shè)置是一個(gè)字典讼渊,鍵為中間件的路徑,值為中間件的順序(order)尊剔。

樣例:

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware' : 543,
}

SPIDER_MIDDLEWARES設(shè)置會(huì)與Scrapy定義的SPIDER_MIDDLEWARES_BASE設(shè)置合并(但不是覆蓋)爪幻,而后根據(jù)順序(order)進(jìn)行排序,最后得到啟用中間件的有序列表须误;第一個(gè)中間件是最靠近引擎的挨稿,最后一個(gè)中間就愛(ài)你是最靠近spider的。

關(guān)于如何分配中間的順序請(qǐng)查看SPIDER_MIDDLEWARES_BASE設(shè)置京痢,而后根據(jù)您想要放置中間件的位置選擇一個(gè)值奶甘。由于每個(gè)中間件執(zhí)行不同的動(dòng)作,您的中間件可能會(huì)依賴于之前(或者之后)執(zhí)行的中間件历造,因此順序是最重要的甩十。

如果您想禁止內(nèi)置的(在SPIDER_MIDDLEWARES_BASE中設(shè)置并默認(rèn)啟用的)中間件船庇,您必須在項(xiàng)目的SPIDER_MIDDLEWARES設(shè)置中定義該中間件吭产,并將其賦值為None侣监,例如,如果您想要關(guān)閉off-site中間件:

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
    'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware': None,
}

最后臣淤,請(qǐng)注意橄霉,有些中間件需要通過(guò)特定的設(shè)置來(lái)啟用。更多內(nèi)容請(qǐng)查看相關(guān)中間件文檔邑蒋。

2. 編寫自己的spider中間件

編寫中間件十分簡(jiǎn)單姓蜂,每個(gè)中間件組件是一個(gè)定義了以下一個(gè)或多個(gè)方法的Python類:

class scrapy.contrib.spidermiddleware.SpiderMiddleware 
process_spider_input(response, spider) 

當(dāng)response通過(guò)spider中間件時(shí),該方法被調(diào)用医吊,處理該response钱慢。

process_spider_input()
應(yīng)該返回一個(gè)None或者拋出一個(gè)異常(exception)。

  • 如果其返回None卿堂,Scrapy將會(huì)繼續(xù)處理該response束莫,調(diào)用所有其他中間件直到spider處理該response。
  • 如果其拋出一個(gè)異常(exception),Scrapy將不會(huì)調(diào)用任何其他中間件的process_spider_input()方法草描,并調(diào)用request的errback览绿。errback的輸出將會(huì)以另一個(gè)方向被輸入到中間鏈中,使用process_spider_output()方法來(lái)處理穗慕,當(dāng)其拋出異常時(shí)則帶調(diào)用process_spider_exception()饿敲。

參數(shù):
response(Response對(duì)象) - 被處理的response
spider(Spider對(duì)象) - 該response對(duì)應(yīng)的spider

process_spider_out(response, result, spider)
當(dāng)Spider處理response返回result時(shí),該方法被調(diào)用逛绵。

process_spider_output()必須返回包含Request或Item對(duì)象的可迭代對(duì)象(iterable)怀各。

參數(shù):
response(Response對(duì)象) - 生成該輸出的response
result(包含Reques或Item對(duì)象的可迭代對(duì)象(iterable)) - spider返回的result
spider(Spider對(duì)象) - 其結(jié)果被處理的spider

process_spider_exception(response, exception, spider)

當(dāng)spider或(其它spider中間件的)process_spider_input()拋出異常時(shí),該方法被調(diào)用

process_spider_exception()必須要么返回None术浪,要么返回一個(gè)包含Response或Item對(duì)象的可迭代對(duì)象(iterable)渠啤。

通過(guò)其返回None,Scrapy將繼續(xù)處理該異常添吗,調(diào)用中間件鏈中的其它中間件的process_spider_exception()
如果其返回一個(gè)可迭代對(duì)象沥曹,則中間件鏈的process_spider_output()方法被調(diào)用,其他的process_spider_exception()將不會(huì)被調(diào)用碟联。

response(Response對(duì)象) - 異常被拋出時(shí)被處理的response
exception(Exception對(duì)象) - 被拋出的異常
spider(Spider對(duì)象) - 拋出異常的spider

process_start_requests(start_requests, spider)
該方法以spider啟動(dòng)的request為參數(shù)被調(diào)用妓美,執(zhí)行的過(guò)程類似于process_spider_output(),只不過(guò)其沒(méi)有相關(guān)聯(lián)的response并且必須返回request(不是item)鲤孵。

其接受一個(gè)可迭代的對(duì)象(start_requests參數(shù))且必須返回一個(gè)包含Request對(duì)象的可迭代對(duì)象壶栋。

當(dāng)在您的spider中間件實(shí)現(xiàn)該方法時(shí),您必須返回一個(gè)可迭代對(duì)象(類似于參數(shù)start_requests)且不要遍歷所有的start_requests普监。該迭代器會(huì)很大(甚至是無(wú)限)贵试,進(jìn)而導(dǎo)致內(nèi)存溢出琉兜。Scrapy引擎再其具有能力處理start_requests時(shí)將會(huì)拉起request,因此start_requests迭代器會(huì)變得無(wú)限,而由其它參數(shù)來(lái)停止spider(例如時(shí)間限制或者item/page計(jì)數(shù))毙玻。

參數(shù):  
start_requests(b包含Request的可迭代對(duì)象) - start requests
spider(Spider對(duì)象) - start request所屬的spider

案例:下載妹子圖圖片

編寫spider實(shí)現(xiàn)代碼

1. spider文件:

##file:MeizituSpider.py
#-*- coding:utf-8 -*-
import scrapy
from scrapy.spiders import Request
import logging
import re
from cnblogSpider.items import SaveGirlImageItem

logger = logging.getLogger(__name__)

class MeiziTuSpider(scrapy.Spider):
    name = "meizitu"
    allowed_domains = ['meizitu.com']
    user_header = {
        "Referer": "http://www.meizitu.com/tag/nvshen_460_1.html",
        "Upgrade-Insecure-Requests" : "1",
        "User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0"
    }

    def start_requests(self):
        logging.debug("###### 妹子圖Spider開始啟動(dòng).....%s"%self)
        return [Request(url="http://www.meizitu.com/tag/nvshen_460_1.html", callback=self.parse, headers = self.user_header)]

    @staticmethod
    def __remove_html_tags(str):
        return re.sub(r'<[^>]+>', '', str)


    def parse(self, response):
        # print(response.body)
        for picdiv in response.css('div[class="pic"]'):
            image_urls = picdiv.css('a[target="_blank"] img::attr(src)').extract_first()
            image_split = image_urls.split("/")
            image_name = image_split[-3]+ image_split[-2]+ image_split[-1]
            yield SaveGirlImageItem({
                'name' : MeiziTuSpider.__remove_html_tags(picdiv.css('a[target="_blank"] img::attr(alt)').extract()[0]),#獲取這組相片的名稱
                'url' : picdiv.css('a[target="_blank"] img::attr(src)').extract_first(),  #獲取這組照片的鏈接
                'image_urls' : [picdiv.css('a[target="_blank"] img::attr(src)').extract_first()],
                'images' : image_name
            })

        next_page = response.xpath(u'//div[@class="navigation"]//li/a[contains(.,"下一頁(yè)")]/@href').extract_first()

        if next_page is not None:
            requesturl = "http://www.meizitu.com" + next_page
            yield Request(requesturl, callback = self.parse, headers=self.user_header)


2. 中間件代碼:

##file:middlewares.py
import logging
###下面是妹子圖案例的spider中間件
logger = logging.getLogger(__name__)

##start_requests函數(shù)調(diào)用這個(gè)spider中間件
class ModifyStartRequest(object):

    def process_start_requests(self, start_requests, spider):
        logging.info("#### 22222222 #####strat_requests %s, spider %s ####"%(start_requests, spider))
        last_request = []
        for one_request in start_requests:
            logging.info("#### one_request %s, spider %s ####"%(one_request, spider))
            last_request.append(one_request)
        logging.info("#### 2222222 ####last_request %s, spider %s ####"%(last_request, spider))
        return last_request
#file:spiderMiddleware.py
import logging

logger = logging.getLogger(__name__)
###
class SpiderInputMiddleware(object):
    def process_spider_input(self, response, spider):
        logging.info("#### 3333 response %s, spider %s ####"%(response, spider))
        return

class SpiderOutputMiddleware(object):
    def process_spider_output(self, response, result, spider):
        logging.info("#### 4444 response %s, spider %s ####" %(response, spider))
        return result

3. item文件:

#file:items.py
class SaveGirlImageItem(scrapy.Item):
    name = scrapy.Field()
    url = scrapy.Field()
    image_urls = scrapy.Field()
    images = scrapy.Field()

4. settings設(shè)置:

#file:settings.py
LOG_LEVEL = "INFO"

#禁用Cookie
COOKIES_ENABLED = False

#spider中間件
SPIDER_MIDDLEWARES = {
   # 'cnblogSpider.middlewares.CnblogspiderSpiderMiddleware': 543,
    'cnblogSpider.middlewares.ModifyStartRequest' : 643,
    'cnblogSpider.spiderMiddleware.SpiderInputMiddleware' : 743,
    'cnblogSpider.spiderMiddleware.SpiderOutputMiddleware': 843
}

#管道中間件
ITEM_PIPELINES = {
    'cnblogSpider.pipelines.MeizituPipelineJson' :10,
    'scrapy.pipelines.images.ImagesPipeline' : 1
}

#使用圖片管道文件下載圖片
IMAGES_STORE="/home/chenqi/python/python_code/python_Spider/chapter04/cnblogs/cnblogSpider/cnblogSpider/images"

IMAGES_URLS_FIELD = "image_urls"
IMAGES_RESULT_FIELD="images"

參考

  1. 代碼目錄
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末豌蟋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子桑滩,更是在濱河造成了極大的恐慌梧疲,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件运准,死亡現(xiàn)場(chǎng)離奇詭異幌氮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)胁澳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門该互,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人韭畸,你說(shuō)我怎么就攤上這事宇智。” “怎么了陆盘?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵普筹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我隘马,道長(zhǎng)太防,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任酸员,我火速辦了婚禮蜒车,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘幔嗦。我一直安慰自己酿愧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布邀泉。 她就那樣靜靜地躺著嬉挡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪汇恤。 梳的紋絲不亂的頭發(fā)上庞钢,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音因谎,去河邊找鬼基括。 笑死,一個(gè)胖子當(dāng)著我的面吹牛财岔,可吹牛的內(nèi)容都是我干的风皿。 我是一名探鬼主播河爹,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼桐款!你這毒婦竟也來(lái)了咸这?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鲁僚,失蹤者是張志新(化名)和其女友劉穎炊苫,沒(méi)想到半個(gè)月后裁厅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冰沙,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年执虹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拓挥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡袋励,死狀恐怖侥啤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情茬故,我是刑警寧澤盖灸,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站磺芭,受9級(jí)特大地震影響赁炎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钾腺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一徙垫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧放棒,春花似錦姻报、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至厢破,卻和暖如春荣瑟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溉奕。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工褂傀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人加勤。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓仙辟,卻偏偏與公主長(zhǎng)得像同波,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叠国,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,655評(píng)論 4 46
  • 下載器中間件是介于Scrapy的request/response處理的鉤子框架未檩,是用于全局修改Scrapy req...
    cnkai閱讀 3,466評(píng)論 0 2
  • 今天第一天進(jìn)入簡(jiǎn)書,希望會(huì)遇見(jiàn)不一樣的自己粟焊,嗯冤狡,現(xiàn)在是凌晨5點(diǎn)31分,失眠已經(jīng)3项棠、4個(gè)小時(shí)了悲雳,不過(guò)我覺(jué)得值,我是個(gè)...
    匠女閱讀 202評(píng)論 0 0
  • 偶讀韋莊知奇葩香追, 初見(jiàn)相認(rèn)溫哥華合瓢。 離血合歡千古愛(ài), 遙化天邊一抹霞透典。 虞舜南巡去不歸晴楔,二妃相誓死江湄。 空...
    珠江潮平閱讀 248評(píng)論 32 29
  • 想要生成一個(gè)自定義范圍內(nèi)的隨機(jī)整數(shù)峭咒,語(yǔ)言之間的差異還真不是一般的大税弃。python用慣了,直接random.rand...
    九十九度中閱讀 459評(píng)論 0 4