You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Scrapy XML Exporter中序列化列表类型的Item字段

我之前刚好碰到过这个问题,Scrapy默认的XmlItemExporter确实会把嵌套的Item用<value>标签包裹,要替换成对应Item的名称,核心就是重写它的serialize_field方法,同时处理好多值字段里的嵌套Item场景。下面给你完整的实现方案:

自定义XmlItemExporter实现子Item标签替换

步骤1:编写自定义导出器类

我们需要继承XmlItemExporter,重写serialize_field方法,针对Item类型(包括单个Item和Item列表)做特殊处理,替换默认的<value>标签为对应Item的名称:

from scrapy.exporters import XmlItemExporter
from scrapy.item import Item

class CustomXmlItemExporter(XmlItemExporter):
    # 可选:自定义Item类名到标签名的映射,让标签更简洁
    ITEM_TAG_MAP = {
        'ReadingAssignment': 'reading',
        'Lesson': 'lesson'
    }

    def serialize_field(self, field, name, value):
        # 先处理多值字段中的Item列表
        if isinstance(value, list) and all(isinstance(item, Item) for item in value):
            for item in value:
                # 获取自定义标签名,没有映射则用类名小写
                item_tag = self.ITEM_TAG_MAP.get(item.__class__.__name__, item.__class__.__name__.lower())
                self._beautify_newline()
                self._write_start_tag(item_tag, self._get_item_attrs(item))
                # 递归处理Item的子字段
                for sub_field_name, sub_field in item.fields.items():
                    sub_value = item.get(sub_field_name)
                    self.serialize_field(sub_field, sub_field_name, sub_value)
                self._beautify_newline()
                self._write_end_tag(item_tag)
            return
        
        # 处理单个嵌套Item的情况(比如Lesson里的assignment字段)
        elif isinstance(value, Item):
            item_tag = self.ITEM_TAG_MAP.get(value.__class__.__name__, value.__class__.__name__.lower())
            self._beautify_newline()
            self._write_start_tag(item_tag, self._get_item_attrs(value))
            for sub_field_name, sub_field in value.fields.items():
                sub_value = value.get(sub_field_name)
                self.serialize_field(sub_field, sub_field_name, sub_value)
            self._beautify_newline()
            self._write_end_tag(item_tag)
            return
        
        # 其他字段(普通单值、非Item列表)调用父类默认处理
        super().serialize_field(field, name, value)
    
    def _get_item_attrs(self, item):
        # 可选:给Item标签添加属性,这里默认返回空字典
        return {}

步骤2:在Pipeline中使用自定义导出器

在你的项目pipelines.py里,替换默认的XmlItemExporter为我们自定义的类:

from scrapy.exceptions import DropItem
from your_project.exporters import CustomXmlItemExporter  # 替换成你的项目路径

class CustomXmlExportPipeline:
    def __init__(self):
        self.file = None
        self.exporter = None

    def open_spider(self, spider):
        self.file = open('courses_export.xml', 'wb')
        # 初始化自定义导出器,设置根标签和Item标签
        self.exporter = CustomXmlItemExporter(
            self.file,
            root_element='courses',
            item_element='course'
        )
        self.exporter.start_exporting()

    def close_spider(self, spider):
        self.exporter.finish_exporting()
        self.file.close()

    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

记得在settings.py中启用这个Pipeline:

ITEM_PIPELINES = {
    'your_project.pipelines.CustomXmlExportPipeline': 300,
}

步骤3:测试效果

假设你的测试数据是:

reading = ReadingAssignment(textBook="Python Basics", pages="1-20")
lesson = Lesson(session="Session 1", topic="Introduction", assignment=reading)
course = Course(title="Python 101", lessons=[lesson])

默认导出器会生成这样的XML:

<courses>
  <course>
    <title>Python 101</title>
    <lessons>
      <value>
        <session>Session 1</session>
        <topic>Introduction</topic>
        <assignment>
          <value>
            <textBook>Python Basics</textBook>
            <pages>1-20</pages>
          </value>
        </assignment>
      </value>
    </lessons>
  </course>
</courses>

使用自定义导出器后,会得到你期望的结果:

<courses>
  <course>
    <title>Python 101</title>
    <lessons>
      <lesson>
        <session>Session 1</session>
        <topic>Introduction</topic>
        <assignment>
          <reading>
            <textBook>Python Basics</textBook>
            <pages>1-20</pages>
          </reading>
        </assignment>
      </lesson>
    </lessons>
  </course>
</courses>

关键说明

  • 核心逻辑是在serialize_field中优先判断值是否为Item/Item列表,跳过默认的<value>标签生成逻辑,直接渲染对应Item名称的标签。
  • 通过ITEM_TAG_MAP可以灵活自定义标签名称,不需要完全依赖类名的小写形式。
  • 递归处理嵌套Item,确保多层嵌套的子Item都能正确替换标签。

内容的提问来源于stack exchange,提问作者jox58

火山引擎 最新活动