使用 python 中的 xml 树将嵌套的 XML 内容转换为 CSV

Convert nested XML content into CSV using xml tree in python

我是 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 树上参考了很多堆栈溢出问题,但仍然没有帮助我。

任何 help/suggestion 表示赞赏。

我在理解这段代码应该做什么时遇到了问题,因为它使用了像 itemchildsubchild 这样的抽象变量名,这使得很难推理代码。我没有那么聪明,所以我将变量重命名为 rowtagcolumn,以便我更容易看到代码在做什么。 (在我的书中,甚至 rowcolumn 都有些抽象,但我想 XML 输入的不透明度几乎不是你的故障。)

你有 2 行但你想要 5 个字典,因为你有 5 个 <column> 标签并且你希望每个 <column> 的数据在一个单独的字典中。但是您希望 <row> 中的 other 标签与每个 <column> 的数据一起重复。

这意味着您需要为每个 <row> 构建一个字典,然后,为每个 <column> 添加 该列的数据到字典中,然后在继续下一栏之前输出它。

此代码做出了一个简化假设,即您的所有 <columns> 都具有相同的结构,只有一个 <question> 和一个 <answer>,没有其他任何东西。如果这个假设不成立,那么 <column> 可能会得到报告,其中包含从同一行中的前一个 <column> 继承的陈旧数据。对于没有至少一个 <column>.

的任何 <row>,它也不会产生任何输出。

代码必须遍历标签两次,一次针对非 <column>s,一次针对 <column>s。否则它不能确定它在开始输出 <column>s.

之前已经看到所有非 <column> 标签

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

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'}]