Scrapy Item 加载器
Item 加载器提供了一种方便的方式来填充从网站上抓取的项目。
声明 Item 加载器
Item 加载器的声明类似于 Items。
例如
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join
class DemoLoader(ItemLoader):
default_output_processor = TakeFirst()
title_in = MapCompose(unicode.title)
title_out = Join()
size_in = MapCompose(unicode.strip)
# you can continue scraping here
在上面的代码中,我们可以看到输入处理器使用 _in
后缀声明,输出处理器使用 _out
后缀声明。
ItemLoader.default_input_processor
和 ItemLoader.default_output_processor
属性用于声明默认输入/输出处理器。
使用 Item 加载器填充项目
要使用 Item 加载器,首先使用类似 dict 的对象或不使用 ItemLoader.default_item_class
属性中指定的 Item 类的对象进行实例化。
我们可以使用选择器将值收集到项目加载器中。
我们可以在同一项目字段中添加更多值,项目加载器将在其中使用适当的处理程序来添加这些值。
以下代码演示了如何使用 Item 加载器填充项目
from scrapy.loader import ItemLoader
from demoproject.items import Demo
def parse(self, response):
l = ItemLoader(item = Product(), response = response)
l.add_xpath("title", "//div[@class = 'product_title']")
l.add_xpath("title", "//div[@class = 'product_name']")
l.add_xpath("desc", "//div[@class = 'desc']")
l.add_css("size", "div#size]")
l.add_value("last_updated", "yesterday")
return l.load_item()
如上所示,有两个不同的 XPath,使用 add_xpath()
方法从中提取 title 字段
1. //div[@class = "product_title"]
2. //div[@class = "product_name"]
此后,对 desc 字段使用类似的请求。 使用 add_css()
方法提取大小数据,并使用 add_value()
方法为 last_updated 填充值“yesterday”。
收集完所有数据后,调用 ItemLoader.load_item()
方法,该方法返回填充有使用 add_xpath()
、add_css()
和 add_value()
方法提取的数据的项目。
输入和输出处理器
Item 加载器的每个字段包含一个输入处理器和一个输出处理器。
- 提取数据后,输入处理器对其进行处理,并将其结果存储在
ItemLoader
中。 - 接下来,收集数据后,调用
ItemLoader.load_item()
方法来获取填充的 Item 对象。 - 最后,我们可以将输出处理器的结果分配给项目。
以下代码演示了如何为特定字段调用输入和输出处理器
l = ItemLoader(Product(), some_selector)
l.add_xpath("title", xpath1) # [1]
l.add_xpath("title", xpath2) # [2]
l.add_css("title", css) # [3]
l.add_value("title", "demo") # [4]
return l.load_item() # [5]
- 第 1 行 - title 的数据从 xpath1 中提取并通过输入处理器传递,其结果被收集并存储在
ItemLoader
中。 - 第 2 行 - 类似地,标题从 xpath2 中提取并通过相同的输入处理器,其结果被添加到为
[1]
收集的数据中。 - 第 3 行 - 标题从 css 选择器中提取并通过相同的输入处理器,结果被添加到为
[1]
和[2]
收集的数据中。 - 第 4 行 - 接下来,值“demo”被分配并通过输入处理器传递。
- 第 5 行 - 最后,从所有字段内部收集数据并传递给输出处理器,最终值分配给项目。
声明输入和输出处理器
输入和输出处理器在 ItemLoader
定义中声明。 除此之外,它们还可以在项目字段元数据中指定。
例如
import scrapy
from scrapy.loader.processors import Join, MapCompose, TakeFirst
from w3lib.html import remove_tags
def filter_size(value):
if value.isdigit():
return value
class Item(scrapy.Item):
name = scrapy.Field(
input_processor = MapCompose(remove_tags),
output_processor = Join(),
)
size = scrapy.Field(
input_processor = MapCompose(remove_tags, filter_price),
output_processor = TakeFirst(),
)
>>> from scrapy.loader import ItemLoader
>>> il = ItemLoader(item = Product())
>>> il.add_value('title', [u'Hello', u'<strong>world</strong>'])
>>> il.add_value('size', [u'<span>100 kg</span>'])
>>> il.load_item()
显示结果如下
{'title': u'Hello world', 'size': u'100 kg'}
Item 加载器上下文
Item 加载器上下文是输入和输出处理器之间共享的任意键值的字典。
例如,假设我们有一个函数 parse_length
def parse_length(text, loader_context):
unit = loader_context.get('unit', 'cm')
# You can write parsing code of length here
return parsed_length
通过接收 loader_context
参数,它告诉 Item 加载器它可以接收 Item 加载器上下文。 有几种方法可以更改 Item 加载器上下文的值 -
- 修改当前活动的 Item 加载器上下文
loader = ItemLoader (product) loader.context ["unit"] = "mm"
- 在 Item 加载器实例化上
loader = ItemLoader(product, unit = "mm")
- 在使用 Item 加载器上下文实例化的输入/输出处理器的 Item 加载器声明上
class ProductLoader(ItemLoader): length_out = MapCompose(parse_length, unit = "mm")
ItemLoader 对象
它是一个对象,它返回一个新的项目加载器来填充给定的项目。 它有以下类
class scrapy.loader.ItemLoader([item, selector, response, ]**kwargs)
下表显示了 ItemLoader 对象的参数
序号 | 参数 | 描述 |
---|---|---|
1 | item | 它是通过调用 add_xpath() 、add_css() 或 add_value() 来填充的项目。 |
2 | selector | 它用于从网站提取数据。 |
3 | response | 它用于使用 default_selector_class 构造选择器。 |
下面是 ItemLoader
对象的方法
序号 | 方法 | 描述 |
---|---|---|
1 | get_value(value, *processors, **kwargs) | 通过给定的处理器和关键字参数,该值由 get_value() 方法处理。 |
2 | add_value(field_name, value, *processors, **kwargs) | 它处理值并在通过字段输入处理器传递之前通过提供处理器和关键字参数将其添加到首先通过 get_value 传递的字段。 |
3 | replace_value(field_name, value, *processors, **kwargs) | 它用新值替换收集的数据。 |
4 | get_xpath(xpath, *processors, **kwargs) | 它用于通过接收 XPath 提供处理器和关键字参数来提取 unicode 字符串。 |
5 | add_xpath(field_name, xpath, *processors, **kwargs) | 它接收到提取 unicode 字符串的字段的 XPath。 |
6 | replace_xpath(field_name, xpath, *processors, **kwargs) | 它使用 XPath 替换从站点收集的数据。 |
7 | get_css(css, *processors, **kwargs) | 它接收用于提取 unicode 字符串的 CSS 选择器。 |
8 | add_css(field_name, css, *processors, **kwargs) | 它类似于 add_value() 方法,唯一不同的是它向字段添加了 CSS 选择器。 |
9 | replace_css(field_name, css, *processors, **kwargs) | 它使用 CSS 选择器替换提取的数据。 |
10 | load_item() | 收集数据时,此方法用收集的数据填充项目并将其返回。 |
11 | nested_xpath(xpath) | 它用于创建带有 XPath 选择器的嵌套加载器。 |
12 | nested_css(css) | 它用于创建带有 CSS 选择器的嵌套加载器。 |
下表显示了 ItemLoader
对象的属性
序号 | 属性 | 描述 |
---|---|---|
1 | item | 它是 Item 加载器执行解析的对象。 |
2 | context | 它是活动的 Item 加载器的当前上下文。 |
3 | default_item_class | 如果未在构造函数中给出,则它用于表示项目。 |
4 | default_input_processor | 未指定输入处理器的字段是唯一使用 default_input_processors 的字段。 |
5 | default_output_processor | 未指定输出处理器的字段是唯一使用 default_output_processors 的字段。 |
6 | default_selector_class | 如果在构造函数中没有给出,它是一个用于构造选择器的类。 |
7 | selector | 它是一个可用于从站点中提取数据的对象。 |
嵌套加载器
它用于在解析文档子部分的值时创建嵌套加载器。 如果不创建嵌套加载器,则需要为要提取的每个值指定完整的 XPath 或 CSS。
例如,假设数据是从标题页中提取的
<header>
<a class = "social" href = "http://facebook.com/whatever">facebook</a>
<a class = "social" href = "http://twitter.com/whatever">twitter</a>
<a class = "email" href = "mailto:someone@example.com">send mail</a>
</header>
接下来,我们可以通过将相关值添加到标头来创建带有标头选择器的嵌套加载器
loader = ItemLoader(item = Item())
header_loader = loader.nested_xpath('//header')
header_loader.add_xpath('social', 'a[@class = "social"]/@href')
header_loader.add_xpath('email', 'a[@class = "email"]/@href')
loader.load_item()
重用和扩展 Item 加载器
Item 加载器旨在减轻维护,当我们的项目获得更多蜘蛛时,维护成为一个基本问题。
例如,假设某个站点的产品名称包含在三个破折号中(例如 --DVD---)。 如果我们不想在最终产品名称中使用它,我们可以通过重复使用默认的产品项目加载器来删除这些破折号,如以下代码所示
from scrapy.loader.processors import MapCompose
from demoproject.ItemLoaders import DemoLoader
def strip_dashes(x):
return x.strip('-')
class SiteSpecificLoader(DemoLoader):
title_in = MapCompose(strip_dashes, DemoLoader.title_in)
可用的内置处理器
以下是一些常用的内置处理器
class scrapy.loader.processors.Identity
它返回原始值而不改变它。 例如
>>> from scrapy.loader.processors import Identity
>>> proc = Identity()
>>> proc(['a', 'b', 'c'])
['a', 'b', 'c']
class scrapy.loader.processors.TakeFirst
它返回接收值列表中的第一个非空/非空值。 例如
>>> from scrapy.loader.processors import TakeFirst
>>> proc = TakeFirst()
>>> proc(['', 'a', 'b', 'c'])
'a'
class scrapy.loader.processors.Join(separator = u' ')
它返回附加到分隔符的值。 默认分隔符是 u' '
,它等同于函数 u' '.join
。 例如
>>> from scrapy.loader.processors import Join
>>> proc = Join()
>>> proc(['a', 'b', 'c'])
u'a b c'
>>> proc = Join('<br>')
>>> proc(['a', 'b', 'c'])
u'a<br>b<br>c'
class scrapy.loader.processors.Compose(*functions, **default_loader_context)
它由处理器定义,其中每个输入值都传递给第一个函数,该函数的结果传递给第二个函数,依此类推,直到最后一个函数返回最终值作为输出。
例如
>>> from scrapy.loader.processors import Compose
>>> proc = Compose(lambda v: v[0], str.upper)
>>> proc(['python', 'scrapy'])
'PYTHON'
class scrapy.loader.processors.MapCompose(*functions, **default_loader_context)
它是一个处理器,其中迭代输入值并将第一个函数应用于每个元素。 接下来,将这些函数调用的结果连接起来构建新的可迭代对象,然后将其应用于第二个函数,依此类推,直到最后一个函数。
例如
>>> def filter_scrapy(x):
return None if x == 'scrapy' else x
>>> from scrapy.loader.processors import MapCompose
>>> proc = MapCompose(filter_scrapy, unicode.upper)
>>> proc([u'hi', u'everyone', u'im', u'pythonscrapy'])
[u'HI, u'IM', u'PYTHONSCRAPY']
class scrapy.loader.processors.SelectJmes(json_path)
此类使用提供的 json
路径查询值并返回输出。
例如
>>> from scrapy.loader.processors import SelectJmes, Compose, MapCompose
>>> proc = SelectJmes("hello")
>>> proc({'hello': 'scrapy'})
'scrapy'
>>> proc({'hello': {'scrapy': 'world'}})
{'scrapy': 'world'}
下面是代码,通过导入json查询值
>>> import json
>>> proc_single_json_str = Compose(json.loads, SelectJmes("hello"))
>>> proc_single_json_str('{"hello": "scrapy"}')
u'scrapy'
>>> proc_json_list = Compose(json.loads, MapCompose(SelectJmes('hello')))
>>> proc_json_list('[{"hello":"scrapy"}, {"world":"env"}]')
[u'scrapy']