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 PipelineItemitemadapter 默认支持的类之一。

Item 必须声明 Field 属性,这些属性会被处理并存储在 fields 属性中。这限制了允许的字段名称集合并防止拼写错误,在引用未定义字段时会引发 KeyError。此外,字段可用于定义元数据并控制数据在内部的处理方式。有关更多信息,请参阅有关 字段 的文档。

dict 实例不同,Item 实例可以被 跟踪 以调试内存泄漏。

copy() Self[源代码]
deepcopy() Self[源代码]

返回此 Item 的 deepcopy()

fields: dict[str, Field] = {}

一个字典,包含此 Item 的 所有已声明字段,而不仅是已填充的字段。键是字段名称,值是在 Item 声明 中使用的 Field 对象。

Item 对象复制了标准的 dict API,包括其 __init__ 方法。

Item 允许定义字段名称,以便

  • 使用未定义字段名称时会引发 KeyError(即防止拼写错误被忽略)

  • Item 导出器 默认可以导出所有字段,即使第一个爬取到的对象并没有所有字段的值

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 还允许您

示例

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)

注意

熟悉 Django 的用户会注意到 Scrapy Item 的声明方式类似于 Django 模型,但 Scrapy Item 更简单,因为它没有不同字段类型的概念。

声明字段

Field 对象用于指定每个字段的元数据。例如,上面示例中 last_updated 字段的序列化函数。

您可以为每个字段指定任何类型的元数据。 Field 对象接受的值没有限制。出于同样的原因,没有所有可用元数据键的参考列表。Field 对象中定义的每个键都可能被不同的组件使用,并且只有这些组件知道它。您也可以在您的项目中定义和使用任何其他 Field 键,以满足您自己的需求。Field 对象的主要目标是提供一种将所有字段元数据定义在一个地方的方式。通常,那些行为取决于每个字段的组件会使用某些字段键来配置其行为。您必须参考它们的文档才能了解每个组件使用了哪些元数据键。

重要的是要注意,用于声明 Item 的 Field 对象不会保留为类属性。相反,可以通过 fields 属性访问它们。

class scrapy.Field[源代码]

字段元数据的容器

Field 类只是内置 dict 类的一个别名,不提供任何额外的功能或属性。换句话说,Field 对象就是普通的 Python 字典。使用一个单独的类是为了支持基于类属性的 Item 声明语法

注意

字段元数据也可以为 dataclassattrs Item 声明。有关更多信息,请参阅 dataclasses.fieldattr.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 类型的代码。