抓取的主要目的是从非结构化源(通常是网页)中提取结构化数据。Scrapy Spider可以将提取的数据作为Python字典返回。Python字典虽然方便且熟悉,但缺乏结构:很容易在字段名称中输入错误或返回不一致的数据,尤其是在具有许多蜘蛛的大型项目中。

为了定义常见的输出数据格式,Scrapy提供了Item类。item对象是用于收集抓取数据的简单容器。它们提供了类似于字典的API,具有方便的语法来声明其可用字段。

1.声明Item

Item 使用简单的 class 定义语法以及 Field 对象来声明。例如:

import scrapy

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)

熟悉 Django 的朋友一定会注意到 Scrapy Item 定义方式与 Django Models 很类似, 不过没有那么多不同的字段类型(Field type),更为简单。

2.Item字段

Field对象指明了每个字段的元数据(metadata)。例如下面例子中last_updated中指明了该字段的序列化函数。

您可以为每个字段指明任何类型的元数据。Field对象对接受的值没有任何限制。也正是因为这个原因,文档也无法提供所有可用的元数据的键(key)参考列表。Field 对象中保存的每个键可以由多个组件使用,并且只有这些组件知道这个键的存在。您可以根据自己的需求,定义使用其他的 Field 键。 设置 Field 对象的主要目的就是在一个地方定义好所有的元数据。一般来说,那些依赖某个字段的组件肯定使用了特定的键(key)。您必须查看组件相关的文档,查看其用了哪些元数据键(metadata key)。

需要注意的是,用来声明itemField对象并没有被赋值为class的属性。不过您可以通过 Item.fields 属性进行访问。

3.Item处理项目

接下来以下边声明的 Product item 来演示一些 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

设置field值

>>> 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')]

复制items

要复制item,您必须确定是要浅拷贝还是深拷贝

#浅拷贝
product2 = product.copy() or product2 = Product(product)

#深拷贝
product2 = product.deepcopy()

4.其他操作

item转化成dict

>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}

dict转化 item

>>> 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'

5.扩展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

6.Item对象

class scrapy.item.Item([arg])

返回一个根据给定的参数可选初始化的item

Item复制了标准的 dict API。包括初始化函数也相同。Item 唯一额外添加的属性是:fields

一个包含了item 所有声明的字段的字典,而不仅仅是获取到的字段。该字典的 key是字段(field)的名字,值是Item声明中使用到的 Field 对象。

7.Field对象

class scrapy.item.Field([arg])

Field 仅仅是内置的 dict 类的一个别名,并没有提供额外的方法或者属性。换句话说,Field 对象完完全全就是 Python 字典(dict)。被用来基于类属性(class attribute)的方法来支持 item 声明语法。

Scrapy 1.7

Scrapy 安装并创建运行爬虫项目(1) Scrapy 命令行工具(2) Scrapy 选择器(3) Scrapy Item(4) Scrapy Item Loaders(5) Scrapy Item Pipeline(6) Scrapy Feed exports(7) Scrapy 架构概览(8) Scrapy 手写一个爬虫(9) Scrapy 知乎模拟登陆 (10) Scrapy源码分析 架构概览(1) Scrapy源码分析 运行入口(2) Scrapy源码分析 核心组件初始化(3) Scrapy源码分析 核心抓取流程(4)