- Scrapy 教程
- Scrapy - 首页
- Scrapy 基本概念
- Scrapy - 概述
- Scrapy - 环境配置
- Scrapy - 命令行工具
- Scrapy - 爬虫 (Spiders)
- Scrapy - 选择器 (Selectors)
- Scrapy - 项目 (Items)
- Scrapy - 项目加载器
- Scrapy - Shell
- Scrapy - 项目管道 (Item Pipeline)
- Scrapy - 数据导出 (Feed exports)
- Scrapy - 请求 & 响应 (Requests & Responses)
- Scrapy - 链接提取器 (Link Extractors)
- Scrapy - 设置 (Settings)
- Scrapy - 异常处理 (Exceptions)
- Scrapy 实战项目
- Scrapy - 创建项目
- Scrapy - 定义项目
- Scrapy - 第一个爬虫
- Scrapy - 爬取数据
- Scrapy - 提取项目数据
- Scrapy - 使用项目数据
- Scrapy - 跟踪链接
- Scrapy - 爬取数据
- Scrapy 有用资源
- Scrapy - 快速指南
- Scrapy - 有用资源
- Scrapy - 讨论
Scrapy - 项目加载器
描述
项目加载器提供了一种方便的方式来填充从网站上抓取的项目数据。
声明项目加载器
项目加载器的声明方式与项目 (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 属性用于声明默认的输入/输出处理器。
使用项目加载器填充项目
要使用项目加载器,首先使用类似字典的对象进行实例化,或者不使用任何对象,在这种情况下,项目使用在 ItemLoader.default_item_class 属性中指定的 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() 方法提取,last_updated 使用 add_value() 方法填充值为 "yesterday"。
一旦所有数据收集完毕,调用 ItemLoader.load_item() 方法,该方法返回使用 add_xpath()、add_css() 和 add_value() 方法提取数据的填充项目。
输入和输出处理器
项目加载器的每个字段包含一个输入处理器和一个输出处理器。
提取数据时,输入处理器会处理数据,并将结果存储在 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 行** - 从 xpath1 提取 title 的数据,并通过输入处理器,将其结果收集并存储在 ItemLoader 中。
**第 2 行** - 同样,从 xpath2 提取 title 并通过相同的输入处理器,将其结果添加到为 [1] 收集的数据中。
**第 3 行** - 从 css 选择器提取 title 并通过相同的输入处理器,将其结果添加到为 [1] 和 [2] 收集的数据中。
**第 4 行** - 接下来,分配值 "demo" 并通过输入处理器。
**第 5 行** - 最后,从所有字段内部收集数据并传递给输出处理器,并将最终值分配给 Item。
声明输入和输出处理器
输入和输出处理器在 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'}
项目加载器上下文
项目加载器上下文是一个任意键值对的字典,在输入和输出处理器之间共享。
例如,假设您有一个函数 `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` 参数,它告诉项目加载器它可以接收项目加载器上下文。有几种方法可以更改项目加载器上下文的值:
修改当前活动的项目加载器上下文:
loader = ItemLoader (product) loader.context ["unit"] = "mm"
在项目加载器实例化时:
loader = ItemLoader(product, unit = "mm")
在项目加载器的输入/输出处理器的声明中,使用项目加载器上下文进行实例化:
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()` 方法处理。 |
>>> from scrapy.loader.processors import TakeFirst >>> loader.get_value(u'title: demoweb', TakeFirst(), unicode.upper, re = 'title: (.+)') 'DEMOWEB` |
| 2 |
它处理值并添加到字段中,首先通过 `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 |
它用新值替换收集到的数据。 |
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 |
它用于通过接收 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 |
它接收 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 |
它使用网站上的 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 |
它接收 CSS 选择器,用于提取 Unicode 字符串。 |
loader.get_css("div.item-name")
loader.get_css("div#length", TakeFirst(),
re = "the length is (.*)")
|
| 8 |
它类似于 `add_value()` 方法,区别在于它向字段添加 CSS 选择器。 |
loader.add_css('name', 'div.item-name')
loader.add_css('length', 'div#length',
re = 'the length is (.*)')
|
| 9 |
它使用 CSS 选择器替换提取的数据。 |
loader.replace_css('name', 'div.item-name')
loader.replace_css('length', 'div#length',
re = 'the length is (.*)')
|
| 10 |
当数据收集完毕时,此方法使用收集到的数据填充项目并返回它。 |
def parse(self, response):
l = ItemLoader(item = Product(),
response = response)
l.add_xpath('title', '//
div[@class = "product_title"]')
loader.load_item()
|
| 11 |
它用于使用 XPath 选择器创建嵌套加载器。 |
loader = ItemLoader(item = Item())
loader.add_xpath('social', '
a[@class = "social"]/@href')
loader.add_xpath('email', '
a[@class = "email"]/@href')
|
| 12 |
它用于使用 CSS 选择器创建嵌套加载器。 |
loader = ItemLoader(item = Item())
loader.add_css('social', 'a[@class = "social"]/@href')
loader.add_css('email', 'a[@class = "email"]/@href')
|
下表显示了 ItemLoader 对象的属性:
| 序号 | 属性 & 描述 |
|---|---|
| 1 | item ItemLoader 执行解析的对象。 |
| 2 | context 这是当前活动的 ItemLoader 的上下文。 |
| 3 |
如果在构造函数中未给出,则用于表示项目。 |
| 4 |
仅对未指定输入处理器的字段使用 `default_input_processors`。 |
| 5 |
仅对未指定输出处理器的字段使用 `default_output_processors`。 |
| 6 |
如果在构造函数中未给出,则用于构造选择器的类。 |
| 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()
重用和扩展项目加载器
项目加载器旨在减轻维护工作,当您的项目获得更多爬虫时,这将成为一个根本性问题。
例如,假设一个网站的商品名称用三个短横线括起来(例如 --DVD---)。如果您不想在最终商品名称中包含这些短横线,您可以重用默认的 Product Item Loader,如下面的代码所示:
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']