在咱们执行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