您的位置:首页 > 编程语言 > Python开发

Python爬虫框架Scrapy 学习笔记 7------- scrapy.Item源码剖析

2015-01-07 18:24 1026 查看
在前面的example中,我们知道定义一个Item类很简单,只要继承scrapy.Item,然后添加几个类型为scrapy.Field的对象作为类属性,就像下面这样
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
last_updated = scrapy.Field(serializer=str)


前面我们使用Item的方法也很简单,就像使用一个Dict一样来使用Item。例如在最初的那个spider中:
torrent = TorrentItem()
torrent['url'] = response.url
torrent['name'] = response.xpath("//h1/text()").extract()
torrent['description'] = response.xpath("//div[@id='description']").extract()
torrent['size'] = response.xpath("//div[@id='specifications']/p[2]/text()[2]").extract()


这里有几个问题:
上面的name, price, stock,last_updated真的是类属性吗?

2. 为什么可以像使用字典一样使用Item的实例 ?
3. Filed有什么用?

按着ctrl键,鼠标放在scrapy.Item上,点进去,答案立马呈现在眼前。
item.py源码
"""
Scrapy Item

See documentation in docs/topics/item.rst
"""

from pprint import pformat
from UserDict import DictMixin

from scrapy.utils.trackref import object_ref

class BaseItem(object_ref):
"""Base class for all scraped items."""
pass

class Field(dict):
"""Container of field metadata"""

class ItemMeta(type):

def __new__(mcs, class_name, bases, attrs):
fields = {}
new_attrs = {}
for n, v in attrs.iteritems():
if isinstance(v, Field):
fields
= v
else:
new_attrs
= v

cls = super(ItemMeta, mcs).__new__(mcs, class_name, bases, new_attrs)
cls.fields = cls.fields.copy()
cls.fields.update(fields)
return cls

class DictItem(DictMixin, BaseItem):

fields = {}

def __init__(self, *args, **kwargs):
self._values = {}
if args or kwargs:  # avoid creating dict for most common case
for k, v in dict(*args, **kwargs).iteritems():
self[k] = v

def __getitem__(self, key):
return self._values[key]

def __setitem__(self, key, value):
if key in self.fields:
self._values[key] = value
else:
raise KeyError("%s does not support field: %s" %
(self.__class__.__name__, key))

def __delitem__(self, key):
del self._values[key]

def __getattr__(self, name):
if name in self.fields:
raise AttributeError("Use item[%r] to get field value" % name)
raise AttributeError(name)

def __setattr__(self, name, value):
if not name.startswith('_'):
raise AttributeError("Use item[%r] = %r to set field value" %
(name, value))
super(DictItem, self).__setattr__(name, value)

def keys(self):
return self._values.keys()

def __repr__(self):
return pformat(dict(self))

def copy(self):
return self.__class__(self)

class Item(DictItem):

__metaclass__ = ItemMeta


类Item继承了DictItem,类Item的类实例是由ItemMeta创建的。(关于Python元类的内容可以参考<<Python核心编程>>)

类DictItem模仿了dict的一些方法,并继承了DictMinx使它具有了类似字典的API

ItemMeta的__new__方法就做了两件事:1.将类型为scrapy.Field的属性放入字典fields中。2.将其它属性放入字典new_attrs中。
所以 name, price, stock,last_updated已经不再是类属性,而是被包含在了类属性fields中。

再看scrapy.Field到底是什么
class Field(dict):
"""Container of field metadata"""
不是别的,就是原原本本的dict,只不过换了个名字

Filed的作用是(见官方文档):

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

官方文档:http://scrapy-chs.readthedocs.org/zh_CN/latest/topics/items.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: