我能够从网站上删除数据,但我需要以XML格式导出数据。
为此,我定义了一个类似的序列化器:
class Person(scrapy.Item):
Name = scrapy.Field(serializer=serialize_name)
Location = scrapy.Field()
和XMLExportPipeline一样:
class XmlExportPipeline(object):
def __init__(self):
self.files = {}
@classmethod
def from_crawler(cls, crawler):
pipeline = cls()
crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
return pipeline
def spider_opened(self, spider):
file = open('%s_people.xml' % spider.name, 'w+b')
self.files[spider] = file
self.exporter = XmlItemExporter(file, item_element='Person', root_element='People')
self.exporter.start_exporting()
def spider_closed(self, spider):
...
def process_item(self, person, spider):
self.exporter.export_item(person)
return person
这可以工作,并给我一个像这样的XML文件:
<?xml version="1.0" encoding="utf-8"?>
<People><Person><Name>Bob</Name><Location>NYC</Location></Person></People>
如何向标签添加属性?例如,如果我想要
<Person Age="25" Likes="Programming">
我该怎么做呢?
同样快速跟进,为什么输出XML没有格式化like it is supposed to be?我可以将标签中的值转换为CDATA(使用自定义序列化程序来执行此操作)吗?
答案 0 :(得分:3)
由于此行(XmlItemExporter
),scrapy/exporters.py:173
的默认实现不允许这样做:
self.xg.startElement(name, {})
第二个参数应该包含每个新元素的属性。因此,解决方法是实现自己的XmlItemExporter
子类,添加此参数。
from scrapy.exporters import six, is_listlike, XmlItemExporter
class AttrXmlItemExporter(XmlItemExporter):
def _export_xml_field(self, name, serialized_value, depth):
# Custom code:
attrs = {}
if isinstance(serialized_value, dict):
serialized_value = serialized_value.copy()
attr_keys = [k for k in serialized_value.keys() if k.startswith('_')]
attrs = {k[1:]: serialized_value.pop(k) for k in attr_keys}
# Default implementation (except for startElement call)
self._beautify_indent(depth=depth)
self.xg.startElement(name, attrs)
if hasattr(serialized_value, 'items'):
self._beautify_newline()
for subname, value in serialized_value.items():
self._export_xml_field(subname, value, depth=depth + 1)
self._beautify_indent(depth=depth)
elif is_listlike(serialized_value):
self._beautify_newline()
for value in serialized_value:
self._export_xml_field('value', value, depth=depth + 1)
self._beautify_indent(depth=depth)
elif isinstance(serialized_value, six.text_type):
self._xg_characters(serialized_value)
else:
self._xg_characters(str(serialized_value))
self.xg.endElement(name)
self._beautify_newline()
在此示例中,任何项目值(即dict以下划线(_
)开头的dict(即子字典)将呈现为属性。
例如,项目:
yield {
'name': 'Sample',
'rating': {
'_rating': '4.5',
'_max': '5',
},
}
将呈现为XML:
<item>
<nam>Sample</name>
<rating rating="4.5" max="5">
</rating>
</item>
但是还没有想出办法让它成为一个自闭元素。请注意,标记为属性的所有值必须为字符串。
答案 1 :(得分:0)
关于您的后续问题之一:当您使用自定义XMLItemExporter时,我相信FEED_EXPORT_INDENT设置会被初始化XMLItemExporter时的kwarg覆盖。如果不传递任何内容,则缩进为“无”。创建indent=<number>
时,您需要传递kwarg。您可以在原始代码中为根元素和项元素加上侧面kwarg来做到这一点,或者,如果您使用已接受答案中的代码,只需用一次调用这些kwarg来覆盖__init__
即可。
关于自动关闭标签的要点:看来解决方案是另一个kwarg short_empty_elements
,这次是签名xml.sax.saxutils.XMLGenerator(out=None, encoding='iso-8859-1', short_empty_elements=False)
的XMLGenerator。我尚未使用此功能,但文档here看起来很简单。