Scrapy源码分析-持久化导出数据类Item Exporters(五)

来源:互联网 发布:单片机io口输出电压 编辑:程序博客网 时间:2024/06/07 13:41

当你抓取了你要的数据(Items),你希望能合适的保存爬取到的数据,或者说,生成一个带有爬取数据的”输出文件”(通常叫做”输出feed”),来供其他系统使用。

Scrapy自带了Feed输出,并且支持多种序列化格式(serialization format)及存储方式(storage backends)。而Feed输出使用到了 ItemExporters

如果你是想单纯的将数据输出或存入文件,那直接可以用Scrapy提供的现成类。如果想要知道Item Exporters是怎么工作的或需要自定义功能,就需要深入了解Item Exporters的运行机制。

为了使用 Item Exporter,你必须对 Item Exporter 及其参数 (args) 实例化。每个 Item Exporter 需要不同的参数 。在实例化了 exporter 之后,你必须:

  1. 调用方法start_exporting()以标识过程的开始。
  2. 对要导出的每个item调用export_item() 方法。
  3. 最后调用finish_exporting()表示过程的结束

BaseItemExporter

classscrapy.contrib.exporter.BaseItemExporter(fields_to_export=Noneexport_empty_fields=False,encoding='utf-8')

  这是一个对所有 Item Exporters 的(抽象)父类。它对所有(具体) Item Exporters 提供基本属性,如定义export什么fields, 是否export空fields, 或是否进行编码。

  你可以在构造器中设置它们不同的属性值: fields_to_export , export_empty_fieldsencoding.

首先在初始化方法__init__(self , **kwargs)中调用_configure(kwargs)方法,提取并设置运行需要的属性。

调用start_exporting(self)方法,为处理item做预处理工作。

当有item到来时,调用export_item(self, item)处理。

处理完所有item后,调用finish_exporting(self)扫尾。

此外还涉及到转码,序列化,分析item的方法。

# 所有Item Exporters的抽象父类,定义了exporter的基本属性 class BaseItemExporter(object):    def __init__(self, **kwargs):        self._configure(kwargs)    def _configure(self, options, dont_fail=False):        #在子类中调用,读取需要export的field给fields_to_export,        # None表示export所有fields. 默认值为None.        self.fields_to_export = options.pop('fields_to_export', None)                #是否在输出数据中包含为空的item fields. 默认值是 False. 一些 exporters (例如 CsvItemExporter) 会忽略此属性并输出所有fields.        self.export_empty_fields = options.pop('export_empty_fields', False)                #Encoding 属性将用于编码 unicode 值. (仅用于序列化字符串).其他值类型将不变的传递到指定的序列化库.        self.encoding = options.pop('encoding', 'utf-8')        if not dont_fail and options:            raise TypeError("Unexpected options: %s" % ', '.join(options.keys()))    #必须在子类中实现。接受一个item,并输出    def export_item(self, item):        raise NotImplementedError        #返回给定field的序列化值。默认使用field中指定的serializer作为序列化函数。如果没有指定serializer且没有指定使用unicode编码到str,返回原值     #你可以覆盖此方法来控制序列化或输出指定的field.    #@param field : 要序列化的field的对象    #@param name  : field的名字    #@param value :field的值    def serialize_field(self, field, name, value):        serializer = field.get('serializer', self._to_str_if_unicode)        return serializer(value)    #表示exporting过程的开始. 一些exporters用于产生需要的头元素(例如 XmlItemExporter). 在实现exporting_item前必须调用此方法.    def start_exporting(self):        pass    #表示exporting过程的结束. 一些exporters用于产生需要的尾元素 (例如 XmlItemExporter). 在完成exporting item后必须调用此方法    def finish_exporting(self):        pass    def _to_str_if_unicode(self, value):        return value.encode(self.encoding) if isinstance(value, unicode) else value    #需要在子类中的export_item调用,读取item,并返回每个filed的键值迭代对象    def _get_serialized_fields(self, item, default_value=None, include_empty=None):        """Return the fields to export as an iterable of tuples (name,        serialized_value)        """        if include_empty is None:            include_empty = self.export_empty_fields        if self.fields_to_export is None:            if include_empty:                field_iter = item.fields.iterkeys()            else:                field_iter = item.iterkeys()        else:            if include_empty:                field_iter = self.fields_to_export            else:                nonempty_fields = set(item.keys())                field_iter = (x for x in self.fields_to_export if x in                              nonempty_fields)        for field_name in field_iter:            if field_name in item:                field = item.fields[field_name]                value = self.serialize_field(field, field_name, item[field_name])            else:                value = default_value            yield field_name, value

以BaseItemExporterScrapy为父类,Scrapy提供了多个Item Exporters子类来创建不同的输出格式,如XML,CSV或JSON格式。

JsonItemExporter

classscrapy.contrib.exporter.JsonItemExporter(file**kwargs)

输出 JSON 文件格式, 所有对象将写进一个对象的列表. 此构造器额外的关键字参数将传给BaseItemExporter 构造器, 其余的将传给 JSONEncoder 构造器, 以此来定制 exporter.

参数:file – 文件类.JSON 是一个简单而有弹性的格式, 但对大量数据的扩展性不是很好,因为这里会将整个对象放入内存. 如果你要JSON既强大又简单,可以考虑 JsonLinesItemExporter , 或把输出对象分为多个块.

class JsonLinesItemExporter(BaseItemExporter):    #指定了输出文件,以及BaseItemExporter所需要的属性。    #ScrapyJSONEncoder提供了将字典类型的数据转换为Json格式数据的方法    def __init__(self, file, **kwargs):        self._configure(kwargs, dont_fail=True)        self.file = file        self.encoder = ScrapyJSONEncoder(**kwargs)            #将转化为字典类型的item写入指定的文件中    #self.file即执行文件的句柄    #这里的file已经在pipeline中打开。    def export_item(self, item):        itemdict = dict(self._get_serialized_fields(item))        self.file.write(self.encoder.encode(itemdict) + '\n')

JsonLinesItemExporter

classscrapy.contrib.exporter.JsonLinesItemExporter(file**kwargs)

输出 JSON 文件格式, 每行写一个 JSON-encoded 项. 此构造器额外的关键字参数将传给BaseItemExporter 构造器, 其余的将传给 JSONEncoder 构造器, 以此来定制 exporter.

参数:file – 文件类.
#创建Json格式的Exporter#与JsonLinesItemExporter不同的是该类会把所有的item封装成数组写入文件,整个json文件就是一个数组。class JsonItemExporter(JsonLinesItemExporter):    def __init__(self, file, **kwargs):        self._configure(kwargs, dont_fail=True)        self.file = file        self.encoder = ScrapyJSONEncoder(**kwargs)        self.first_item = True    def start_exporting(self):        self.file.write("[")    def finish_exporting(self):        self.file.write("]")    def export_item(self, item):        if self.first_item:            self.first_item = False        else:            self.file.write(',\n')        itemdict = dict(self._get_serialized_fields(item))        self.file.write(self.encoder.encode(itemdict))

XmlItemExporter

classscrapy.contrib.exporter.XmlItemExporter(fileitem_element='item'root_element='items',**kwargs)

以XML格式 exports Items 到指定的文件类.

参数:
  • file – 文件类.
  • root_element (str) – XML 根元素名.
  • item_element (str) – XML item 的元素名.

构造器额外的关键字参数将传给 BaseItemExporter 构造器.

#创建XML格式的Exporterclass XmlItemExporter(BaseItemExporter):        #需要指定根元素和item元素名    def __init__(self, file, **kwargs):        self.item_element = kwargs.pop('item_element', 'item')        self.root_element = kwargs.pop('root_element', 'items')        self._configure(kwargs)        self.xg = XMLGenerator(file, encoding=self.encoding)    def start_exporting(self):        self.xg.startDocument()        self.xg.startElement(self.root_element, {})    def export_item(self, item):        self.xg.startElement(self.item_element, {})        for name, value in self._get_serialized_fields(item, default_value=''):            self._export_xml_field(name, value)        self.xg.endElement(self.item_element)    def finish_exporting(self):        self.xg.endElement(self.root_element)        self.xg.endDocument()    def _export_xml_field(self, name, serialized_value):        self.xg.startElement(name, {})        if hasattr(serialized_value, 'items'):            for subname, value in serialized_value.items():                self._export_xml_field(subname, value)        elif hasattr(serialized_value, '__iter__'):            for value in serialized_value:                self._export_xml_field('value', value)        else:            self._xg_characters(serialized_value)        self.xg.endElement(name)    # Workaround for http://bugs.python.org/issue17606    # Before Python 2.7.4 xml.sax.saxutils required bytes;    # since 2.7.4 it requires unicode. The bug is likely to be    # fixed in 2.7.6, but 2.7.6 will still support unicode,    # and Python 3.x will require unicode, so ">= 2.7.4" should be fine.    if sys.version_info[:3] >= (2, 7, 4):        def _xg_characters(self, serialized_value):            if not isinstance(serialized_value, unicode):                serialized_value = serialized_value.decode(self.encoding)            return self.xg.characters(serialized_value)    else:        def _xg_characters(self, serialized_value):            return self.xg.characters(serialized_value)

还有CsvItemExporter , PickleItemExporter, MarshalItemExporter,PprintItemExporter,PythonItemExporter等。Scrapy提供这些Item Exporter基本大同小异,代码也不难,这里不再一一列出。

有兴趣的可以私下交流。


0 0