Item
爬取的主要目标是从非结构化来源(通常是网页)中提取结构化数据。 爬虫 可以以 Item 的形式返回提取的数据,Item 是定义键值对的 Python 对象。
Scrapy 支持 多种 Item 类型。创建 Item 时,可以使用任何想要的 Item 类型。编写接收 Item 的代码时,您的代码应该 适用于任何 Item 类型。
Item 类型
Scrapy 通过 itemadapter 库支持以下 Item 类型:字典、Item 对象、dataclass 对象 和 attrs 对象。
字典 (Dictionaries)
作为 Item 类型,dict
方便且易于熟悉。
Item 对象
Item
提供了一个类似 dict
的 API 以及额外的功能,使其成为功能最齐全的 Item 类型
- class scrapy.Item(*args: Any, **kwargs: Any)[源代码]
爬取 Item 的基类。
在 Scrapy 中,如果一个对象受 itemadapter 库支持,则它被视为一个
item
。例如,评估爬虫回调的输出时,只有此类对象会被传递给 Item Pipeline。Item
是 itemadapter 默认支持的类之一。Item 必须声明
Field
属性,这些属性会被处理并存储在fields
属性中。这限制了允许的字段名称集合并防止拼写错误,在引用未定义字段时会引发KeyError
。此外,字段可用于定义元数据并控制数据在内部的处理方式。有关更多信息,请参阅有关 字段 的文档。与
dict
实例不同,Item
实例可以被 跟踪 以调试内存泄漏。- deepcopy() Self [源代码]
返回此 Item 的
deepcopy()
。
Item
对象复制了标准的 dict
API,包括其 __init__
方法。
Item
允许定义字段名称,以便
trackref
会跟踪 Item
对象以帮助查找内存泄漏(参见 使用 trackref 调试内存泄漏)。
示例
from scrapy.item import Item, Field
class CustomItem(Item):
one_field = Field()
another_field = Field()
Dataclass 对象
版本 2.2 中新增。
dataclass()
允许定义带有字段名称的 Item 类,以便 Item 导出器 默认可以导出所有字段,即使第一个爬取到的对象并没有所有字段的值。
此外,dataclass
Item 还允许您
定义每个已定义字段的类型和默认值。
通过
dataclasses.field()
定义自定义字段元数据,可用于 自定义序列化。
示例
from dataclasses import dataclass
@dataclass
class CustomItem:
one_field: str
another_field: int
注意
字段类型在运行时不强制执行。
attr.s 对象
版本 2.2 中新增。
attr.s()
允许定义带有字段名称的 Item 类,以便 Item 导出器 默认可以导出所有字段,即使第一个爬取到的对象并没有所有字段的值。
此外,attr.s
Item 还允许
要使用此类型,需要安装 attrs 包。
示例
import attr
@attr.s
class CustomItem:
one_field = attr.ib()
another_field = attr.ib()
使用 Item 对象
声明 Item 子类
Item 子类使用简单的类定义语法和 Field
对象来声明。这是一个示例
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
tags = scrapy.Field()
last_updated = scrapy.Field(serializer=str)
声明字段
Field
对象用于指定每个字段的元数据。例如,上面示例中 last_updated
字段的序列化函数。
您可以为每个字段指定任何类型的元数据。 Field
对象接受的值没有限制。出于同样的原因,没有所有可用元数据键的参考列表。Field
对象中定义的每个键都可能被不同的组件使用,并且只有这些组件知道它。您也可以在您的项目中定义和使用任何其他 Field
键,以满足您自己的需求。Field
对象的主要目标是提供一种将所有字段元数据定义在一个地方的方式。通常,那些行为取决于每个字段的组件会使用某些字段键来配置其行为。您必须参考它们的文档才能了解每个组件使用了哪些元数据键。
重要的是要注意,用于声明 Item 的 Field
对象不会保留为类属性。相反,可以通过 fields
属性访问它们。
- class scrapy.Field[源代码]
字段元数据的容器
Field
类只是内置dict
类的一个别名,不提供任何额外的功能或属性。换句话说,Field
对象就是普通的 Python 字典。使用一个单独的类是为了支持基于类属性的 Item 声明语法。
注意
字段元数据也可以为 dataclass
和 attrs
Item 声明。有关更多信息,请参阅 dataclasses.field 和 attr.ib 的文档。
使用 Item 对象
以下是使用上面 声明的 Product
Item 进行的一些常见任务的示例。您会注意到其 API 与 dict
API 非常相似。
创建 Item
>>> product = Product(name="Desktop PC", price=1000)
>>> print(product)
Product(name='Desktop PC', price=1000)
获取字段值
>>> product["name"]
Desktop PC
>>> product.get("name")
Desktop PC
>>> product["price"]
1000
>>> product["last_updated"]
Traceback (most recent call last):
...
KeyError: 'last_updated'
>>> product.get("last_updated", "not set")
not set
>>> product["lala"] # getting unknown field
Traceback (most recent call last):
...
KeyError: 'lala'
>>> product.get("lala", "unknown field")
'unknown field'
>>> "name" in product # is name field populated?
True
>>> "last_updated" in product # is last_updated populated?
False
>>> "last_updated" in product.fields # is last_updated a declared field?
True
>>> "lala" in product.fields # is lala a declared field?
False
设置字段值
>>> product["last_updated"] = "today"
>>> product["last_updated"]
today
>>> product["lala"] = "test" # setting unknown field
Traceback (most recent call last):
...
KeyError: 'Product does not support field: lala'
访问所有已填充的值
要访问所有已填充的值,只需使用典型的 dict
API
>>> product.keys()
['price', 'name']
>>> product.items()
[('price', 1000), ('name', 'Desktop PC')]
复制 Item
要复制 Item,您必须首先决定是需要浅拷贝还是深拷贝。
如果您的 Item 包含像列表或字典这样的 可变 值,则浅拷贝将保留对所有不同副本中相同可变值的引用。
例如,如果您的 Item 包含一个标签列表,并且您创建了该 Item 的浅拷贝,则原始 Item 和拷贝都拥有相同的标签列表。向其中一个 Item 的列表添加标签,也会将该标签添加到另一个 Item 中。
如果这不是期望的行为,请改用深拷贝。
有关更多信息,请参阅 copy
。
要创建 Item 的浅拷贝,您可以调用现有 Item 的 copy()
方法 (product2 = product.copy()
) 或从现有 Item 实例化您的 Item 类 (product2 = Product(product)
)。
要创建深拷贝,请改用 deepcopy()
(product2 = product.deepcopy()
)。
其他常见任务
从 Item 创建字典
>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}
Creating items from dicts:
>>> Product({"name": "Laptop PC", "price": 1500})
Product(price=1500, name='Laptop PC')
>>> Product({"name": "Laptop PC", "lala": 1500}) # warning: unknown field in dict
Traceback (most recent call last):
...
KeyError: 'Product does not support field: lala'
扩展 Item 子类
您可以通过声明原始 Item 的子类来扩展 Item(添加更多字段或更改某些字段的元数据)。
例如
class DiscountedProduct(Product):
discount_percent = scrapy.Field(serializer=str)
discount_expiration_date = scrapy.Field()
您还可以通过使用先前的字段元数据并追加更多值,或更改现有值来扩展字段元数据,如下所示
class SpecificProduct(Product):
name = scrapy.Field(Product.fields["name"], serializer=my_serializer)
这会添加(或替换)name
字段的 serializer
元数据键,同时保留所有先前存在的元数据值。
支持所有 Item 类型
在接收 Item 的代码中,例如 Item Pipeline 或 爬虫中间件 的方法中,最佳实践是使用 ItemAdapter
类来编写适用于任何受支持 Item 类型的代码。