第十篇 scrapy item loader機制


在我們執行scrapy爬取字段中,會有大量的和下面的代碼,當要爬取的網站多了,要維護起來很麻煩,為解決這類問題,我們可以根據scrapy提供的loader機制

    def parse_detail(self, response):
        """
        獲取文章詳情頁
        :param response:
        :return:
        """
        article_item = JoBoleArticleItem()

        #封面圖,使用get方法有個好處,如果圖片不存在。不會拋異常。
        front_image_url = response.meta.get("front_image_url","")
        ret_str = response.xpath('//*[@class="dht_dl_date_content"]')
        title = response.css("div.entry-header h1::text").extract_first()
        create_date = response.css("p.entry-meta-hide-on-mobile::text").extract_first().strip().replace("·", "").strip()
        content = response.xpath("//*[@id='post-112239']/div[3]/div[3]/p[1]")
        article_item["title"] = title

首先,導入 ItemLoader 

from scrapy.loader import ItemLoader

 可以查看源碼,這里先關注的是item和response兩入參

 

        #通過item loader加載item
        item_loader = ItemLoader(item=JoBoleArticleItem(),response=response)
        #針對直接取值的情況
        item_loader.add_value('front_image_url','front_image_url')
        #針對css選擇器
        item_loader.add_css('title','div.entry-header h1::text')
        item_loader.add_css('create_date','p.entry-meta-hide-on-mobile::text')
        item_loader.add_css('praise_num','#112547votetotal::text')
        #針對xpath的情況
        item_loader.add_xpath('content','//*[@id="post-112239"]/div[3]/div[3]/p[1]')
        #把結果返回給item對象
        article_item = item_loader.load_item()

debug調試,可以看到拿到的信息

 

不過實際情況,可能1、我們只取返回結果的某個元素。2、拿到返回結果后還需要執行某些函數。 這個scrapy也提供了方法:

在items.py文件里操作,這里用到了MapCompose類

from scrapy.loader.processors import MapCompose

 這個類我們可以傳遞任意多的函數進來處理

導入模塊后,

 在Field的入參里可以傳入這個函數,方式如下,其中MapCompose里填的是函數名,而調用的這個alter_title函數的入參,就是title的拿到的值,即input_processor參數的效果是在傳值給item前進行預處理

def alter_title(value):
    return value + "-白菜被豬拱了"

class JoBoleArticleItem(scrapy.Item):
    #標題
    title = scrapy.Field(
        input_processor = MapCompose(alter_title)
    )

 debug調試下,

 在loader機制中也有類似extract_firest的方法:TakeFirst

from scrapy.loader.processors import MapCompose,TakeFirst

然后在下面的:

經測試 input_processor和output_processor同時存在時,會把input進行預處理拿到的返回值繼續給output處理,返回最終結果給item

import datetime
def date_convert(value):
    try :
        create_date = datetime.datetime.strftime(value,"%Y/%m/%d").date()
    except Exception as e:
        create_date = datetime.datetime.now().date()

    return create_date



class JoBoleArticleItem(scrapy.Item):
    #標題
    title = scrapy.Field(
        input_processor = MapCompose(alter_title)
    )
    #創建日期
    create_date  = scrapy.Field(
        # = MapCompose(date_convert),
        input_processor = MapCompose(date_convert),
        output_processor = TakeFirst()
    )

如果要每個字段都要單獨調用這個TakeFirst方法,會有些麻煩,可以通過自定義ItemLoader,首先導入ItemLoader進行重載

from scrapy.loader import ItemLoader

 點開ItemLoader源碼,可以查看到有個default_output_processor

 然后我們給ItemLoader重載這個default_output_processor

class ArticleItemLoader(ItemLoader):
    #自定義ItemLoader
    default_output_processor = TakeFirst()

 

然后在創建itemloader對象時使用自定義的loader:ArticleItemLoader

        item_loader = ArticleItemLoader(item=JoBoleArticleItem(),response=response)
        #針對直接取值的情況
        item_loader.add_value('front_image_url','front_image_url')
        item_loader.add_value('front_image_path','')
        item_loader.add_value('url',response.url)
        item_loader.add_value('url_object_id',get_md5(response.url))
        item_loader.add_value('content','')

debug調試,可以看到獲取到的value由list變成str 

 

PS:這里只是把默認的output_processor制定了一個方法,所以如果存在某些item 不想調用默認的output_processor,可以繼續在add_value方法里單獨傳output方法。

 

 

 

 

 

 

問題:

1、調試時遇到下面這錯誤,一般是由於傳遞給items.py的數據里缺少了字段、傳遞的字段和數據表里的字段的類型不符等

2

 

3、使用itemloader爬取時,返回的數據類型是list,再存入item容器前,是支持對數據進行預處理的,即輸入處理器和輸出處理器,可以通過MapCompose這個類來依次對list的元素進行處理,

但如果lsit為【】則不會進行處理,這種情況需要重載MapCompose類的__call__方法,如下,我給vallue增加一個空的str“”

class MapComposeCustom(MapCompose):
    #自定義MapCompose,當value沒元素是傳入""
    def __call__(self, value, loader_context=None):
        if not value:
            value.append("")
        values = arg_to_iter(value)
        if loader_context:
            context = MergeDict(loader_context, self.default_loader_context)
        else:
            context = self.default_loader_context
        wrapped_funcs = [wrap_loader_context(f, context) for f in self.functions]
        for func in wrapped_funcs:
            next_values = []
            for v in values:
                next_values += arg_to_iter(func(v))
            values = next_values
        return values

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM