在咱們執行scrapy爬取字段中,會有大量的和下面的代碼,當要爬取的網站多了,要維護起來很麻煩,爲解決這類問題,咱們能夠根據scrapy提供的loader機制css
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 app
from scrapy.loader import ItemLoader
能夠查看源碼,這裏先關注的是item和response兩入參scrapy
#經過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調試,能夠看到拿到的信息ide
不過實際狀況,可能一、咱們只取返回結果的某個元素。二、拿到返回結果後還須要執行某些函數。 這個scrapy也提供了方法:函數
在items.py文件裏操做,這裏用到了MapCompose類post
from scrapy.loader.processors import MapCompose
這個類咱們能夠傳遞任意多的函數進來處理測試
導入模塊後,網站
在Field的入參裏能夠傳入這個函數,方式以下,其中MapCompose裏填的是函數名,而調用的這個alter_title函數的入參,就是title的拿到的值,即input_processor參數的效果是在傳值給item前進行預處理url
def alter_title(value): return value + "-白菜被豬拱了" class JoBoleArticleItem(scrapy.Item): #標題 title = scrapy.Field( input_processor = MapCompose(alter_title) )
debug調試下,spa
在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方法。
問題:
一、調試時遇到下面這錯誤,通常是因爲傳遞給items.py的數據裏缺乏了字段、傳遞的字段和數據表裏的字段的類型不符等
2
三、使用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