天天看点

Scrapy学习笔记VI--Item Loaders

Item Loaders

理解:Items为爬取的数据提供容器,而Item Loaders为容器填充数据(提取数据的路径、规则等等……为了方便,便于管理、扩展)

可以先阅读最下方的几个例子,在来一 一对照复习用法

from scrapy.loader import ItemLoader
from myproject.items import Product     # 项目中已经定义的items 类

def parse(self, response):
    l = ItemLoader(item=Product(), response=response)    #参数为Product类数据模板(已经定义好的容器) ,服务器返回的Response
    l.add_xpath('name', '//div[@class="product_name"]')   #定义数据提取路径,收集并保存在item loaders中,还未存入item数据模板中
    l.add_xpath('name', '//div[@class="product_title"]') #定义数据提取路径,收集并保存在item loaders中,还未存入item数据模板中
    l.add_xpath('price', '//p[@id="price"]') #收集price,定义数据提取路径,收集并保存在item loaders中,还未存入item数据模板中
    l.add_css('stock', 'p#stock]')    #收集stock,定义数据提取路径,收集并保存在item loaders中,还未存入item数据模板中use literal values   #直接定义last_updated的数据,可为literal(迭代),
    return l.load_item()   #收集所有数据后,返回已填充的数据,并保存在items的数据模板中(各个field)
           
  • Input and Output processors

    每一个字段都包含一个Input(收集数据存入列表中)和output(存入items的各个字段中)的过程,如上述例子。

    对象中的第一个参数必须是可迭代的,如“name”,“price”……

  • Declaring item loaders(声明item loaders)
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join

class ProductLoader(ItemLoader):    #定义下载器类

    default_output_processor = TakeFirst()   #定义默认的output processor,还可自己定义 

    name_in = MapCompose(unicode.title)  # 以"_in"后缀定义input
    name_out = Join()   #以"_out"后缀定义output

    price_in = MapCompose(unicode.strip)

    # ...
           
  • Declaring Input and Output Processors #定义input和output的 processor
#定义item字段
import scrapy
from scrapy.loader.processors import Join, MapCompose, TakeFirst
from w3lib.html import remove_tags

def filter_price(value):     #判断value是否为数字
    if value.isdigit():
        return value

class Product(scrapy.Item):
    name = scrapy.Field(
        input_processor=MapCompose(remove_tags),
        output_processor=Join(),
    )
    price = scrapy.Field(
        input_processor=MapCompose(remove_tags, filter_price),    #定义price字段中的input processor 收集规则
        output_processor=TakeFirst(),  #定义price字段中output的存入item的规则
    ) 
           
>>> from scrapy.loader import ItemLoader
>>> il = ItemLoader(item=Product())  #item下载器初始化
>>> il.add_value('name', [u'Welcome to my', u'<strong>website</strong>'])
>>> il.add_value('price', [u'&euro;', u'<span>1000</span>'])
>>> il.load_item()
{'name': u'Welcome to my website', 'price': u'1000'}
           

input和output各定义的优先级:

1. field_in 和 field_out
 2. Field metadata (input_processor and output_processor key)
           

price = scrapy.Field( input_processor=MapCompose(remove_tags, filter_price), #定义price字段中的input processor 收集规则 output_processor=TakeFirst(), #定义price字段中output的存入item的规则 )

3.  ItemLoader.default_input_processor() and ItemLoader.default_output_processor()
           
  • Item Loader Context (需进一步理解, item loader 的指令环境?)

常用来改变input/output processor 的行为,传入指令,如str.upper(把字符串变大写)

在input和output processors中共享的一对字典(任意键值)

在 Item loader中指出可以接受Item Loader Context指令(通过传入参数 loader_context)

  • ItemLoader objects

这些参数指定了loader context(loader的指令环境? 通过context属性)

get_xxx   
add_xxx

load_item()  为item填充数据,并且返回。通过

outprocesor获取到value并分配到指定的item field

nested_xpath  嵌套的Xpath提取

get_collected_values(field_name)  #获得field字段的值

get_output_value(field_name)     #通过out processor获得field的字段值 ,并不对item填充数据或者改变

get_input_processor(field_name)

get_output_processor(field_name)
           
  • ItemLoader 有以下属性

    item : item对象通过item loader进行解析

context : item loader 当前生效的context(上下文环境)

default_item_class

default_input_processor

default_output_processor

default_selector_class

selector

  • Nested Loaders 嵌套的loaders(加载器?)

    有重复路径时使用

<footer>
    <a class="social" href="http://facebook.com/whatever">Like Us</a>
    <a class="social" href="http://twitter.com/whatever">Follow Us</a>
    <a class="email" href="mailto:[email protected]">Email Us</a>
</footer>
           

不使用nested loaders:

loader = ItemLoader(item=Item())   #初始化ItemLoader 
# load stuff not in the footer
loader.add_xpath('social', '//footer/a[@class = "social"]/@href')   #使用全路径
loader.add_xpath('email', '//footer/a[@class = "email"]/@href')
loader.load_item()
           

使用nested loaders:

loader = ItemLoader(item=Item())
# load stuff not in the footer
footer_loader = loader.nested_xpath('//footer') # 避免重复footer路径
footer_loader.add_xpath('social', 'a[@class = "social"]/@href')
footer_loader.add_xpath('email', 'a[@class = "email"]/@href')
# no need to call footer_loader.load_item()
loader.load_item()
           
  • Reusing and extending Item Loaders

    当整个项目越来越大,越来越臃肿,要优化代码,减少重复代码的出现

  • Available built-in processors

    可用的内置处理器

eg. 1

class scrapy.loader.processors.Identity  #不做任何事情,返回的原始值不变,也不接收loader contexts
>>> from scrapy.loader.processors import Identity
>>> proc = Identity()
>>> proc(['one', 'two', 'three'])
['one', 'two', 'three']
           

eg.2

class scrapy.loader.processors.TakeFirst #从提取数据开始返回第一个不为空的value ,通常使用在output processor 给单个的字段赋值  ,不接收任何参数和 loader context
>>> from scrapy.loader.processors import TakeFirst
>>> proc = TakeFirst()
>>> proc(['', 'one', 'two', 'three'])
'one'
           

eg. 3

class scrapy.loader.processors.Join(separator=u' ')  #把返回的值加入到分离器中提供给结构体,默认使用u" "(加个空格),不接收loader context
 >>> from scrapy.loader.processors import Join
>>> proc = Join()  #默认value之间加个空格
>>> proc(['one', 'two', 'three'])
u'one two three'
>>> proc = Join('<br>')
>>> proc(['one', 'two', 'three'])
u'one<br>two<br>three'   #在每个value中间加入<br>
           

eg. 4

class scrapy.loader.processors.Compose(*functions, **default_loader_context) # 传入方法,loader-context(类似于上下文命令,如下定义str.upper:字符串变大写),当value=None时,process停止,通过改变stop_on_none=False.改变这种行为

>>> from scrapy.loader.processors import Compose
>>> proc = Compose(lambda v: v[], str.upper)  #当传入了loader_context(str.upper)参数,processor就会自动检测到 active Loader Context,并实现
>>> proc(['hello', 'world'])
'HELLO'

#ItemLoader.context() 属性优先于active Loader context
           

eg. 5

class scrapy.loader.processors.MapCompose(*functions, **default_loader_context)   # 和compose函数类似,区别如下:
#1. 传递可迭代的参数   
#2.第一个方法作用于每一个元素,结果会重新集合在一个可迭代的对象中-->然后上一个返回的对象依次调用下一个function,直到列表中的每一个元素都被执行过。最后把由输出到output of the processor   
#3.可以返回一个value,也可返回value的列表,也可返回None  
#4.此函数被用作input processor ,当extract()提取数据并返回开一个Unicode列表时。

>>> def filter_world(x):
...     return None if x == 'world' else x
...
>>> from scrapy.loader.processors import MapCompose
>>> proc = MapCompose(filter_world, unicode.upper)
>>> proc([u'hello', u'world', u'this', u'is', u'scrapy'])
[u'HELLO, u'THIS', u'IS', u'SCRAPY']
           

eg. 6

class scrapy.loader.processors.SelectJmes(json_path) 

>>> from scrapy.loader.processors import SelectJmes, Compose, MapCompose  #函数在同一时间只提供一个输出结果
>>> proc = SelectJmes("foo") #for direct use on lists and dictionaries
>>> proc({'foo': 'bar'})
'bar'
>>> proc({'foo': {'bar': 'baz'}})
{'bar': 'baz'}

json中:
>>> import json
>>> proc_single_json_str = Compose(json.loads, SelectJmes("foo"))
>>> proc_single_json_str('{"foo": "bar"}')
u'bar'
>>> proc_json_list = Compose(json.loads, MapCompose(SelectJmes('foo')))
>>> proc_json_list('[{"foo":"bar"}, {"baz":"tar"}]')
[u'bar']
           

如有错误请指出