【问题标题】:Convert nested XML content into CSV using xml tree in python使用python中的xml树将嵌套的XML内容转换为CSV
【发布时间】:2020-07-03 12:16:54
【问题描述】:

我对 python 很陌生,请同样对待我。当我尝试将 XML 内容转换为字典列表时,我得到了输出,但没有达到预期,并且尝试了很多。

XML 内容

<project>
<data>
    <row>
        <respondent>m0wxo5f6w42h3fot34m7s6xij</respondent>
        <timestamp>10-06-16 11:30</timestamp>
        <product>1</product>
        <replica>1</replica>
        <seqnr>1</seqnr>
        <session>1</session>
        <column>
            <question>Q1</question>
            <answer>a1</answer>
        </column>
        <column>
            <question>Q2</question>
            <answer>a2</answer>
        </column>
    </row>
<row>
        <respondent>w42h3fot34m7s6x</respondent>
        <timestamp>10-06-16 11:30</timestamp>
        <product>1</product>
        <replica>1</replica>
        <seqnr>1</seqnr>
        <session>1</session>
        <column>
            <question>Q3</question>
            <answer>a3</answer>
        </column>
        <column>
            <question>Q4</question>
            <answer>a4</answer>
        </column>
    <column>
            <question>Q5</question>
            <answer>a5</answer>
        </column>
    </row>
</data>
</project>

我使用过的代码:

import xml.etree.ElementTree as ET

tree = ET.parse(xml_file.xml)   # import xml from
root = tree.getroot()  
data_list = []

for item in root.find('./data'):    # find all projects node
  data = {}              # dictionary to store content of each projects
  for child in item:
    data[child.tag] = child.text   # add item to dictionary

#-----------------for loop with subchild is not working as expcted in my case
    for subchild in child:
      data[subchild.tag] = subchild.text
      data_list.append(data)
print(data_list)

headers = {k for d in data_list for k in d.keys()} # headers for csv 
with open(csv_file,'w') as f:
    writer = csv.DictWriter(f, fieldnames = headers)    # creating a DictWriter object
    writer.writeheader()    # write headers to csv
    writer.writerows(data_list)

data_list 的输出是获取字典列表中问题的最后信息。 我想问题出在 subchild forloop 但我不明白如何在列表中附加字典。

[{
  'respondent': 'anonymous_m0wxo5f6w42h3fot34m7s6xij',
  'timestamp': '10-06-16 11:30',
  'product': '1',
  'replica': '1',
  'seqnr': '1',
  'session': '1',
  'column': '\n  ,
  'question': 'Q2',
  'answer': 'a2'
},
{
'respondent': 'w42h3fot34m7s6x',
  'timestamp': '10-06-16 11:30',
  'product': '1',
  'replica': '1',
  'seqnr': '1',
  'session': '1',
  'column': '\n ,
  'question': 'Q2',
  'answer': 'a2'
}.......
]

我期待下面的输出,尝试了很多但无法遍历列标签。

[{
    'respondent': 'anonymous_m0wxo5f6w42h3fot34m7s6xij',
    'timestamp': '10-06-16 11:30',
    'product': '1',
    'replica': '1',
    'seqnr': '1',
    'session': '1',
    'question': 'Q1',
    'answer': 'a1'
  },
  {
    'respondent': 'anonymous_m0wxo5f6w42h3fot34m7s6xij',
    'timestamp': '10-06-16 11:30',
    'product': '1',
    'replica': '1',
    'seqnr': '1',
    'session': '1',
    'question': 'Q2',
    'answer': 'a2'
  },
  {
    'respondent': 'w42h3fot34m7s6x',
    'timestamp': '10-06-16 11:30',
    'product': '1',
    'replica': '1',
    'seqnr': '1',
    'session': '1',
    'question': 'Q3',
    'answer': 'a3'
  },
  {
    'respondent': 'w42h3fot34m7s6x',
    'timestamp': '10-06-16 11:30',
    'product': '1',
    'replica': '1',
    'seqnr': '1',
    'session': '1',
    'question': 'Q4',
    'answer': 'a4'
  },
  {
    'respondent': 'w42h3fot34m7s6x',
    'timestamp': '10-06-16 11:30',
    'product': '1',
    'replica': '1',
    'seqnr': '1',
    'session': '1',
    'question': 'Q5',
    'answer': 'a5'
  }
]

我已经在 xml 树上引用了很多堆栈溢出问题,但仍然没有帮助我。

感谢任何帮助/建议。

【问题讨论】:

  • 这个问题和this previous one of yours有关吗?
  • 不,这是新问题,我需要在根的子子节点上循环以制作字典列表并将其转换为 CSV。我提到了一些stackoverflow,但我没有找到如何循环。
  • 好的,那么你能澄清一下到底是什么问题吗?
  • 我需要将行标签相关信息转换为csv,我知道如何转换但我不明白如何将嵌套标签('project/data/row/column')信息一起获取,如图所示在预期的输出。请帮忙
  • 我不明白如何将嵌套标签('project/data/row/column')信息放在一起,如预期的输出所示。这是我们需要更多信息的部分关于。当你努力做到这一点时,你会在某个时候碰壁,对吧?那堵墙是什么?

标签: python xml csv dictionary xml-parsing


【解决方案1】:

我在理解这段代码应该做什么时遇到了问题,因为它使用了抽象变量名称,例如 itemchildsubchild,这使得很难对代码进行推理。我没有那么聪明,所以我将变量重命名为rowtagcolumn,以便我更容易看到代码在做什么。 (在我的书中,即使是 rowcolumn 也有点抽象,但我认为 XML 输入的不透明度几乎不是你的错。)

您有 2 行,但需要 5 个字典,因为您有 5 个 &lt;column&gt; 标记,并且您希望每个 &lt;column&gt; 的数据在一个单独的字典中。但是您希望 &lt;row&gt; 中的 other 标记与每个 &lt;column&gt; 的数据一起重复。

这意味着您需要为每个&lt;row&gt; 构建一个字典,然后,对于每个&lt;column&gt;,将该列的数据添加到字典中,然后在继续下一列之前将其输出.

这段代码做了一个简化的假设,即所有&lt;columns&gt;s 都具有相同的结构,只有一个&lt;question&gt; 和一个&lt;answer&gt;,仅此而已。如果这个假设不成立,那么&lt;column&gt; 可能会被报告为从同一行中的前一个&lt;column&gt; 继承的陈旧数据。对于没有至少一个&lt;column&gt; 的任何&lt;row&gt;,它也不会产生任何输出。

代码必须遍历标签两次,一次用于非&lt;column&gt;s,一次用于&lt;column&gt;s。否则它不能确定它在开始输出&lt;column&gt;s 之前已经看到了所有非&lt;column&gt; 标签。

还有其他(毫无疑问更优雅)的方法可以做到这一点,但我尽可能地让代码结构接近你的原始结构,除了让变量名不那么不透明。

for row in root.find('./data'):    # find all projects node
    data = {}              # dictionary to store content of each projects
    for tag in row:
        if tag.tag != "column":
            data[tag.tag] = tag.text   # add row to dictionary
    # Now the dictionary data is built for the row level
    for tag in row:
        if tag.tag == "column":
            for column in tag:
                data[column.tag] = column.text
            # Now we have added the column level data for one column tag
            data_list.append(data.copy())

输出如下。字典的键顺序没有保留,因为为了方便我使用了pprint.pprint

[{'answer': 'a1',
  'product': '1',
  'question': 'Q1',
  'replica': '1',
  'respondent': 'm0wxo5f6w42h3fot34m7s6xij',
  'seqnr': '1',
  'session': '1',
  'timestamp': '10-06-16 11:30'},
 {'answer': 'a2',
  'product': '1',
  'question': 'Q2',
  'replica': '1',
  'respondent': 'm0wxo5f6w42h3fot34m7s6xij',
  'seqnr': '1',
  'session': '1',
  'timestamp': '10-06-16 11:30'},
 {'answer': 'a3',
  'product': '1',
  'question': 'Q3',
  'replica': '1',
  'respondent': 'w42h3fot34m7s6x',
  'seqnr': '1',
  'session': '1',
  'timestamp': '10-06-16 11:30'},
 {'answer': 'a4',
  'product': '1',
  'question': 'Q4',
  'replica': '1',
  'respondent': 'w42h3fot34m7s6x',
  'seqnr': '1',
  'session': '1',
  'timestamp': '10-06-16 11:30'},
 {'answer': 'a5',
  'product': '1',
  'question': 'Q5',
  'replica': '1',
  'respondent': 'w42h3fot34m7s6x',
  'seqnr': '1',
  'session': '1',
  'timestamp': '10-06-16 11:30'}]

【讨论】:

  • 感谢您的解决方案,它看起来很棒。由于我对 pythonic 的方式和做法非常陌生,从下一次开始我会注意更好地解释。当我执行了上述代码时,我面临一个问题,即在问题列表中的每个字典中,并回答它使用最后一个迭代值(Q2 和 a2)进行更新。你能分享一下输出吗
  • 添加了输出。您报告的不当行为是由于我粗心的剪切粘贴造成的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-16
  • 2016-12-21
  • 1970-01-01
  • 2021-08-24
  • 1970-01-01
相关资源
最近更新 更多