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'€', 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
class scrapy.loader.ItemLoader([item,selector,response,])**kwargs) #返回一个新的itemloader为item填充数据,没有传入item,使用默认的default_item_class
这些参数指定了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:whatever@example.com">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[0], 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']
如有错误请指出