7.2 添加导出数据格式
在某些需求下,我们想要添加新的导出数据格式,此时需要实现新的Exporter类。下面先参考Scrapy内部的Exporter类是如何实现的,然后自行实现一个Exporter。
7.2.1 源码参考
Scrapy内部的Exporter类在scrapy.exporters模块中实现,以下是其中的代码片段:
class BaseItemExporter(object): def __init__(self, **kwargs): self._configure(kwargs) def _configure(self, options, dont_fail=False): self.encoding = options.pop('encoding', None) self.fields_to_export = options.pop('fields_to_export', None) self.export_empty_fields = options.pop('export_empty_fields', False) if not dont_fail and options: raise TypeError("Unexpected options: %s" % ', '.join(options.keys())) def export_item(self, item): raise NotImplementedError def serialize_field(self, field, name, value): serializer = field.get('serializer', lambda x: x) return serializer(value) def start_exporting(self): pass def finish_exporting(self): pass 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 and not isinstance(item, dict): field_iter = six.iterkeys(item.fields) else: field_iter = six.iterkeys(item) else: if include_empty: field_iter = self.fields_to_export else: field_iter = (x for x in self.fields_to_export if x in item) for field_name in field_iter: if field_name in item: field = {} if isinstance(item, dict) else item.fields[field_name] value = self.serialize_field(field, field_name, item[field_name]) else: value = default_value yield field_name, value # json class JsonItemExporter(BaseItemExporter): def __init__(self, file, **kwargs): self._configure(kwargs, dont_fail=True) self.file = file kwargs.setdefault('ensure_ascii', not self.encoding) self.encoder = ScrapyJSONEncoder(**kwargs) self.first_item = True def start_exporting(self): self.file.write(b"[\n") def finish_exporting(self): self.file.write(b"\n]") def export_item(self, item): if self.first_item: self.first_item = False else: self.file.write(b',\n') itemdict = dict(self._get_serialized_fields(item)) data = self.encoder.encode(itemdict) self.file.write(to_bytes(data, self.encoding)) # json lines class JsonLinesItemExporter(BaseItemExporter): ... # xml class XmlItemExporter(BaseItemExporter): ... # csv class CsvItemExporter(BaseItemExporter): ... ...
其中的每一个Exporter都是BaseItemExporter的一个子类,BaseItemExporter定义了一些抽象接口待子类实现:
export_item(self, item)
负责导出爬取到的每一项数据,参数item为一项爬取到的数据,每个子类必须实现该方法。
start_exporting(self)
在导出开始时被调用,可在该方法中执行某些初始化工作。
finish_exporting(self)
在导出完成时被调用,可在该方法中执行某些清理工作。
以JsonItemExporter为例,其实现非常简单:
为了使最终导出结果是一个json中的列表,在start_exporting和finish_exporting方法中分别向文件写入b"[\n, b"\n]"。
在export_item方法中,调用self.encoder.encode方法将一项数据转换成json串(具体细节不再赘述),然后写入文件。
7.2.2 实现Exporter
接下来,我们参照JsonItemExporter的源码,在第1章example项目中实现一个能将数据以Excel格式导出的Exporter。
在项目中创建一个my_exporters.py(与settings.py同级目录),在其中实现ExcelItemExporter,代码如下:
from scrapy.exporters import BaseItemExporter import xlwt class ExcelItemExporter(BaseItemExporter): def __init__(self, file, **kwargs): self._configure(kwargs) self.file = file self.wbook = xlwt.Workbook() self.wsheet = self.wbook.add_sheet('scrapy') self.row = 0 def finish_exporting(self): self.wbook.save(self.file) def export_item(self, item): fields = self._get_serialized_fields(item) for col, v in enumerate(x for _, x in fields): self.wsheet.write(self.row, col, v) self.row += 1
解释上述代码如下:
这里使用第三方库xlwt将数据写入Excel文件。
在构造器方法中创建Workbook对象和Worksheet对象,并初始化用来记录写入行坐标的self.row。
在export_item方法中调用基类的_get_serialized_fields方法,获得item所有字段的迭代器,然后调用self.wsheet.write方法将各字段写入Excel表格。
finish_exporting方法在所有数据都被写入Excel表格后被调用,在该方法中调用self.wbook.save方法将Excel表格写入Excel文件。
完成ExcelItemExporter后,在配置文件settings.py中添加如下代码:
FEED_EXPORTERS = {'excel': 'example.my_exporters.ExcelItemExporter'}
现在,可以使用ExcelItemExporter导出数据了,以-t excel为参数重新运行爬虫:
$ scrapy crawl books -t excel -o books.xls
图7-1所示为爬取完成后在Excel文件中观察到的结果。
图7-1
如上所示,我们成功地使用ExcelItemExporter将爬取到的数据存入了Excel文件中。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论