Scrapy 简明教程
Scrapy - Item Loaders
Declaring Item Loaders
项目加载器的声明类似于项目。
例如 -
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 属性用于声明默认输入/输出处理器。
Using Item Loaders to Populate Items
要使用项目加载器,首先使用类字典对象实例化或没有类字典对象实例化项目,该项目使用 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() 方法抽取的数据的项目。
Input and Output Processors
项目加载器的每个域都包含一个输入处理器和一个输出处理器。
-
在抽取数据时,输入处理器对数据进行处理,其结果存储在 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]
Line 1 - 从 xpath1 抽取标题数据并传递通过输入处理器,然后收集其结果并存储在 ItemLoader 中。
Line 2 - 同样,从 xpath2 抽取标题并传递通过相同的输入处理器,然后将其结果添加到为 [1] 收集的数据中。
Line 3 - 从 css 选择器抽取标题并传递通过相同的输入处理器,然后将结果添加到为 [1] 和 [2] 收集的数据中。
Line 4 - 接下来,分配值“demo”并传递通过输入处理器。
Line 5 - 最后,从所有域内部收集数据并传递至输出处理器,最后的值分配给项目。
Declaring Input and Output Processors
输入和输出处理器在项目加载器的定义中声明。除此外,它们还可以 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'}
Item Loader Context
项目加载器上下文是 input 和 output 处理器共享的任意键值 dict。
例如,假设您具有函数 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 上下文的 value −
-
修改当前的活动 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 Objects
它是一个对象,返回一个 Item Loader,以填充给定的项目。它有以下类 −
class scrapy.loader.ItemLoader([item, selector, response, ]**kwargs)
下表显示 ItemLoader 对象的参数 −
Sr.No |
Parameter & Description |
1 |
item 它是通过调用 add_xpath()、add_css() 或 add_value() 填充的项目。 |
2 |
selector 用于从网站提取数据。 |
3 |
response 用于通过 default_selector_class 构建选择器。 |
下表显示 ItemLoader 对象的方法 −
Sr.No |
Method & Description |
Example |
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 代码:<div class = "item-name">DVD</div>loader.get_xpath("//div[@class =[id="_html_code_div_id_lengththe_length_is"]= HTML 代码:<div id = "length">the length is45cm</div>loader.get_xpath("//div[@id = 'length']", TakeFirst(),re = "the length is (.*)") |
5 |
add_xpath(field_name, xpath, *processors, **kwargs) 它接收要提取 unicode 字符串的字段的 XPath。 |
= HTML 代码:<div class = "item-name">DVD</div> loader.add_xpath('name', '//div [id="_html_code_div_id_lengththe_length_is"] = HTML 代码:<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 代码:<div class = "item-name">DVD</div> loader.replace_xpath('name', ' [id="_html_code_div_id_lengththe_length_is"] = HTML 代码:<div id = "length">the length is 45cm</div> loader.replace_xpath('length', ' |
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) It is similar to add_value() method with one difference that it adds CSS selector to the field. |
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) It replaces the extracted data using CSS selector. |
loader.replace_css('name', 'div.item-name') loader.replace_css('length', 'div#length', re = 'the length is (.*)') |
10 |
load_item() When the data is collected, this method fills the item with collected data and returns it. |
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) It is used to create nested loaders with an XPath selector. |
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 |
Attribute & Description |
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 这是一个可以用于从网站中提取数据的对象。 |
Nested Loaders
它用于在解析文档子部分中的值时创建嵌套加载器。如果你不创建嵌套加载器,你需要为要提取的每个值指定完整的 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()
Reusing and extending 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)
Available Built-in Processors
以下是常用的内置处理器 −
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']