Scrapy - 项目加载器
描述
项目加载器提供了一种方便的方式来填充从网站抓取的项目。
声明项目加载器
项目加载器的声明类似于项目。
例如 −
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 属性用于声明默认输入/输出处理器。
使用项目加载器填充项目
要使用项目加载器,首先使用类似字典的对象进行实例化,或者不使用类似字典的对象,其中项目使用 ItemLoader.default_item_class 属性中指定的项目类。
您可以使用选择器将值收集到项目加载器中。
您可以在同一个项目字段中添加更多值,项目加载器将使用适当的处理程序来添加这些值值。
以下代码演示了如何使用项目加载器填充项目 −
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 的值填充为"昨天"。
收集完所有数据后,调用 ItemLoader.load_item() 方法,该方法返回使用 add_xpath()、add_css() 和 add_value() 方法提取的数据填充的项目。
输入和输出处理器
Item Loader 的每个字段包含一个输入处理器和一个输出处理器。
提取数据后,输入处理器对其进行处理,并将其结果存储在 ItemLoader 中。
接下来,收集数据后,调用 ItemLoader.load_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 行 − 类似地,title 也从 xpath2 中提取出来,经过相同的输入处理器,其结果被添加到为 [1] 收集的数据中。
第 3 行 − title 是从 css 选择器中提取出来的,经过相同的输入处理器,其结果被添加到为 [1] 和 [2] 收集的数据中。
第 4 行 − 接下来,赋值"demo"并经过输入处理器。
第 5 行 −最后,从所有字段内部收集数据并传递给输出处理器,最终值分配给项目。
声明输入和输出处理器
输入和输出处理器在 ItemLoader 定义中声明。除此之外,它们还可以在 Item Field 元数据中指定。
例如 −
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'}
项目加载器上下文
项目加载器上下文是输入和输出处理器之间共享的任意键值的字典。
例如,假设您有一个函数 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 Loader 它可以接收 Item Loader 上下文。有几种方法可以更改 Item Loader 上下文的值 −
修改当前活动的 Item Loader 上下文 −
loader = ItemLoader (product) loader.context ["unit"] = "mm"
在 Item Loader 实例化时 −
loader = ItemLoader(product, unit = "mm")
在 Item Loader 声明中,使用 Item Loader 上下文实例化的输入/输出处理器 −
class ProductLoader(ItemLoader): length_out = MapCompose(parse_length, unit = "mm")
ItemLoader 对象
它是一个返回新项目加载器以填充给定项目的对象。它具有以下类 −
class scrapy.loader.ItemLoader([item, selector, response, ]**kwargs)
下表显示了 ItemLoader 对象的参数 −
Sr.No | 参数与说明 |
---|---|
1 | item 它是通过调用 add_xpath()、add_css() 或 add_value() 来填充的项目。 |
2 | selector 它用于从网站中提取数据。 |
3 | response 它用于使用 default_selector_class 构造选择器。 |
下表显示了 ItemLoader 对象的方法 −
Sr.No | 方法和说明 | 示例 |
---|---|---|
1 | get_value(value, *processors, **kwargs) 通过给定的处理器和关键字参数,该值由 get_value() 方法处理。 |
>>> from scrapy.loader.processors import TakeFirst >>> loader.get_value(u'title: demoweb', TakeFirst(), unicode.upper, re = 'title: (.+)') 'DEMOWEB` |
2 | add_value(field_name, value, *processors, **kwargs) 它处理该值,并通过在通过字段输入处理器之前提供处理器和关键字参数,将其添加到首先通过 get_value 传递的字段。 |
loader.add_value('title', u'DVD') loader.add_value('colors', [u'black', u'white']) loader.add_value('length', u'80') loader.add_value('price', u'2500') |
3 | replace_value(field_name, value, *processors, **kwargs) 它用新值替换收集的数据。 |
loader.replace_value('title', u'DVD') loader.replace_value('colors', [u'black', u'white']) loader.replace_value('length', u'80') loader.replace_value('price', u'2500') |
4 | get_xpath(xpath, *processors, **kwargs) 它用于通过接收 XPath 提供处理器和关键字参数来提取 unicode 字符串。 |
# HTML code: <div class = "item-name">DVD</div> loader.get_xpath("//div[@class = 'item-name']") # HTML code: <div id = "length">the length is 45cm</div> loader.get_xpath("//div[@id = 'length']", TakeFirst(), re = "the length is (.*)") |
5 | add_xpath(field_name, xpath, *processors, **kwargs) 它接收 XPath 到提取 unicode 字符串的字段。 |
# HTML code: <div class = "item-name">DVD</div> loader.add_xpath('name', '//div [@class = "item-name"]') # HTML code: <div id = "length">the length is 45cm</div> loader.add_xpath('length', '//div[@id = "length"]', re = 'the length is (.*)') |
6 | replace_xpath(field_name, xpath, *processors, **kwargs) 使用 XPath 替换从站点收集的数据。 |
# HTML code: <div class = "item-name">DVD</div> loader.replace_xpath('name', ' //div[@class = "item-name"]') # HTML code: <div id = "length">the length is 45cm</div> loader.replace_xpath('length', ' //div[@id = "length"]', re = 'the length is (.*)') |
7 | get_css(css, *processors, **kwargs) 它接收用于提取unicode字符串的CSS选择器。 |
loader.get_css("div.item-name") loader.get_css("div#length", TakeFirst(), re = "the length is (.*)") |
8 | add_css(field_name, css, *processors, **kwargs) 它与add_value()方法类似,但有一点不同,即它将CSS选择器添加到字段中。 |
loader.add_css('name', 'div.item-name') loader.add_css('length', 'div#length', re = 'the length is (.*)') |
9 | replace_css(field_name, css, *processors, **kwargs) 它使用 CSS 选择器替换提取的数据。 |
loader.replace_css('name', 'div.item-name') loader.replace_css('length', 'div#length', re = 'the length is (.*)') |
10 | load_item() 当数据收集到时,此方法将使用收集到的数据填充项目并返回它。 |
def parse(self, response): l = ItemLoader(item = Product(), response = response) l.add_xpath('title', '// div[@class = "product_title"]') loader.load_item() |
11 | nested_xpath(xpath) 它用于使用 XPath 选择器创建嵌套加载器。 |
loader = ItemLoader(item = Item()) loader.add_xpath('social', ' a[@class = "social"]/@href') loader.add_xpath('email', ' a[@class = "email"]/@href') |
12 | nested_css(css) 它用于使用 CSS 选择器创建嵌套加载器。 |
loader = ItemLoader(item = Item()) loader.add_css('social', 'a[@class = "social"]/@href') loader.add_css('email', 'a[@class = "email"]/@href') |
下表显示了 ItemLoader 对象的属性−
Sr.No | 属性和说明 |
---|---|
1 | item 它是 Item Loader 执行解析的对象。 |
2 | context 它是当前处于活动状态的 Item Loader 上下文。 |
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 Loaders
Item Loaders 旨在减轻维护负担,当您的项目获得更多蜘蛛时,维护就成了一个根本问题。
例如,假设某个站点的产品名称用三个破折号括起来(例如 --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']