本文为译文,原文见地址:https://docs.scrapy.org/en/latest/topics/items.html
数据项
本文主要目的是,从非结构化的数据源(比如,web页面)提取出结构化的数据。Scrapy爬虫可以提取数据并返回项Python字典一样的数据。虽然让人觉得很方便和熟悉,但是Python字典缺乏结构化:这是很容易在字段名中输入错误或者返回不一致的数据,特别是在有许多爬虫的大型项目中。
为了定义常见的输出数据格式,Scrapy提供了Item类。Item对象是一种简单的容器,用来搜集爬取到的数据。它提供了一个类似于字典的API,具有方便的语法来声明它们的可用字段。
各种Scrapy组件使用Item提供的额外信息:导出器查看已声明的字段,来找出列进行导出;通过Item字段的元数据使序列化可定制;trackref跟踪Item实例来帮助查找内存泄漏(请参见使用trackref调试内存泄漏);等等。
声明数据项
使用简单的类定义语法和字段对象,来声明数据项。下面是一个示例:
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很类似,除了Scrapy Item没有不同字段类型的概念外。
数据项字段
Field对象被用来指定每个字段的元数据。比如,上面的示例中演示了last_updated字段的序列化函数。
你可以为每个字段指定任何种类的元数据。Field对象接受的值是没有限制的。出于同样的原因,所有可用的元数据关键字没有引用列表。每一个在Field对象中定义的关键字,都可能会被不同的组件使用,并且仅仅只有这些组件知道这些关键字。你也可以根据你的需求,在你的项目中使用其他Field关键字。Field对象的主要目的在于,提供一种方式在某个地方来定义所有的字段元数据。通常而言,那些行为依赖于每个字段的组件使用某些字段关键字来配置该行为。你必须参考它们的文档,以查看每个组件使用哪些元数据关键字。
需要注意的是,用于声明Item的Field对象不会被指定为类属性。相反,可以通过Item.fields属性来访问它们。
使用数据项
这里有一些使用Items的常见使用示例,使用上面声明的Product。你会注意到它们的API与dict API非常相似。
创建数据项
>>> product = Product(name='Desktop PC', price=1000)
>>> print(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'] # 获取未知字段
Traceback (most recent call last):
...
KeyError: 'lala'
>>> product.get('lala', 'unknown field')
'unknown field'
>>> 'name' in product # 是否有name字段
True
>>> 'last_updated' in product # 是否有last_updated字段
False
>>> 'last_updated' in product.fields # last_updated是一个声明了的字段?
True
>>> 'lala' in product.fields # lala是一个声明了的字段
False
设置字段值
>>> product['last_updated'] = 'today'
>>> product['last_updated']
'today'
>>> product['lala'] = 'test' # 设置未知字段
Trackback (most recent call last):
...
KeyError: 'Product does not support field: lala'
访问所有填充的值
为了访问所有填充字段,只需要使用典型的dict API:
>>> product.keys()
dict_keys(['name', 'price', 'last_updated'])
>>> product.items()
ItemsView({'last_updated': 'today', 'name': 'Desktop PC', 'price': 1000})
其他通用任务
复制Item:
>>> product2 = Product(product)
>>> print(product2)
{'last_updated': 'today', 'name': 'Desktop PC', 'price': 1000}
>>> product3 = product2.copy()
>>> print(product3)
{'last_updated': 'today', 'name': 'Desktop PC', 'price': 1000}
从Item创建字典:
>>> dict(product) # 获取所有填充值来创建字典
{'name': 'Desktop PC', 'price': 1000, 'last_updated': 'today'}
从字典创建Item:
>>> Product({'name': 'Laptop PC', 'price': 1500})
{'name': 'Laptop PC', 'price': 1500}
>>> Product({'name': "laptop PC', 'lala': 1500}) # 警告:字典中存在未知字段
Trackback (most recent call last):
...
KeyError: 'Product does not support field: lala'
扩展数据项
你可以通过声明你的原始Item的子类型来扩展Item(为了添加更多字段或者改变一些字段的元数据)。
举个栗子:
class DiscountedProduct(Product):
discount_percent = scrapy.Field(serializer=str)
discount_expiration_date = scrapy.Field()
你也可以通过如下方式扩展字段元数据——使用前面的字段元数据,并添加更多的值,或者改变已存在的值,如下所示:
class SpecificProduct(Product):
name = scrapy.Field(Product.field['name'], serializer=my_serializer)
这样就可以添加(或者替换)name字段的serializer元数据关键字,并保持所有之前已经存在的元数据的值。
Item对象
class scrapy.item.Item([arg])
返回一个根据可选的参数arg初始化而来Item对象。
Item复制标准的dict API,包括其构造函数。项目提供的唯一附加属性是:
fields
是一个包含了这个Item的所有声明字段的字典,不仅仅是已经填充了的字段。关键字是字段名,值是Item声明中使用的Field对象。
Field对象
class scrapy.item.Field([arg])
Field类是一个内置字典类的别名,并且Field类不提供任何额外的功能或者属性。换句话说,Field对象就是Python字典。一个单独的类用于支持基于类属性的Item声明语法。