【问题标题】:complex xml to csv using python [closed]使用python将复杂的xml转换为csv [关闭]
【发布时间】:2021-10-29 07:23:27
【问题描述】:
<app>
<doc>
<field name="id">013</field>
<field name="groupid">013</field>
<field name="img_url">8b4</field>
<field name="filetype">HTML</field>
<field name="url">https://calgaryherald.com/pmn/business-pmn/sally-rumbles-toward-u-s-/</field>
<field name="topic">accurate</field>
<field name="topic">additional</field>
<field name="topic">agriculture</field>
<field name="topic">area</field>
<field name="topic">biggest</field>
</doc>
<doc>
<field name="id">0131</field>
<field name="groupid">013</field>
<field name="img_url">8b</field>
<field name="filetype">HTML</field>
<field name="url">https://calgaryherald.com/pmn/business-pmn/sally-rumbles-toward</field>
<field name="topic">accurate</field>
<field name="topic">additional</field>
<field name="topic">agriculture</field>
<field name="topic">area</field>
<field name="topic">biggest1</field>
<field name="topic">biggest2</field>
<field name="topic">biggest3</field>
</doc>
</app>

我有一个与此类似的 xml,我需要将其转换为 python 中的 csv。有谁知道该怎么做,而且不同文档的字段名称主题也不同,csv标题应该与字段名称相似,主题应该在一个以逗号分隔的单元格中。

预期输出 enter image description here

【问题讨论】:

  • 字段名称是否提前知道?您希望如何在 csv 中存储多个主题。我认为它们应该是单个“主题”列中的某种列表。它们会用逗号分隔吗,有点像嵌入在 csv 单元格中的 csv?
  • @tdelaney 是的,字段名称是事先知道的。并且主题应该用逗号分隔在一个单元格中
  • “主题应该用逗号分隔”是指每一行的列数取决于主题的数量,还是应该有一个主题列,其内部逗号被转义?
  • @tdelaney 主题应该用逗号分隔,并且应该在单列中
  • csv 应该是什么样子? (基于帖子中的xml)。请将此信息添加到帖子中。

标签: python xml csv


【解决方案1】:

您可以使用 XML 解析器在解析构建 csv 时发出元素数据。在每个结束标记上,您可以向行添加值或写入行本身。 iterparse 的一个优点是您不需要在处理之前将整个文档加载到内存中。

import xml.etree.ElementTree as ET
import io
import csv

field_names = ["id", "groupid", "img_url", "filetype", "url", "topic"]
field_names_set = set(field_names)

with open("test.csv", "w", newline="") as out_file:
    writer = csv.DictWriter(out_file, field_names)
    writer.writeheader()
    row = {}
    topic = []
    for event, elem in ET.iterparse("test.xml"): # iterate tag end events
        if elem.tag == "doc":
            # doc elem end, write row to csv and setup for next
            row["topic"] = ",".join(topic)
            writer.writerow(row)
            row = {}
            topic = []            
        elif elem.tag == "field":
            # field elem end, add to current row
            if elem.attrib["name"] == "topic":
                topic.append(elem.text)
            else:
                row[elem.attrib["name"]] = elem.text

【讨论】:

    【解决方案2】:

    下面创建了一个类似 csv 的输出。这就是你要找的吗?
    请注意,您无法分辨哪个字段是“主题”,哪个字段不是“主题”

    import xml.etree.ElementTree as ET
    xml = '''<?xml version="1.0" encoding="UTF-8"?>
    <app>
       <doc>
          <field name="id">013</field>
          <field name="groupid">013</field>
          <field name="img_url">8b4</field>
          <field name="filetype">HTML</field>
          <field name="url">https://calgaryherald.com/pmn/business-pmn/sally-rumbles-toward-u-s-/</field>
          <field name="topic">accurate</field>
          <field name="topic">additional</field>
          <field name="topic">agriculture</field>
          <field name="topic">area</field>
          <field name="topic">biggest</field>
       </doc>
       <doc>
          <field name="id">0131</field>
          <field name="groupid">013</field>
          <field name="img_url">8b</field>
          <field name="filetype">HTML</field>
          <field name="url">https://calgaryherald.com/pmn/business-pmn/sally-rumbles-toward</field>
          <field name="topic">accurate</field>
          <field name="topic">additional</field>
          <field name="topic">agriculture</field>
          <field name="topic">area</field>
          <field name="topic">biggest1</field>
          <field name="topic">biggest2</field>
          <field name="topic">biggest3</field>
       </doc>
    </app>'''
    root = ET.fromstring(xml)
    first_time = True
    headers = set()
    for doc in root.findall('.//doc'):
        data = []
        for field in doc.findall('field'):
            if first_time:
                headers.add(field.attrib['name'])
            data.append((field.attrib['name'], field.text))
        if first_time:
            print(','.join(sorted(list(headers))))
            first_time = False
        print(','.join(y[1] for y in sorted(data, key=lambda x: x[0])))
    

    输出

    filetype,groupid,id,img_url,topic,url
    HTML,013,013,8b4,accurate,additional,agriculture,area,biggest,https://calgaryherald.com/pmn/business-pmn/sally-rumbles-toward-u-s-/
    HTML,013,0131,8b,accurate,additional,agriculture,area,biggest1,biggest2,biggest3,https://calgaryherald.com/pmn/business-pmn/sally-rumbles-toward
    

    【讨论】:

    • 非常感谢,我知道如何格式化它了。
    • @joemarshal 你想与世界分享你想要的输出吗?
    • 我已经在帖子中添加了输出图片
    猜你喜欢
    • 2020-06-10
    • 1970-01-01
    • 2019-12-11
    • 1970-01-01
    • 2020-12-09
    • 2014-08-07
    • 2013-11-14
    • 1970-01-01
    • 2018-02-09
    相关资源
    最近更新 更多