【问题标题】:python merge multiple xmls to single CSVpython将多个xml合并到单个CSV
【发布时间】:2026-01-16 03:40:01
【问题描述】:

这是一个脚本中的一个 sn-p,我正在尝试将其组合在一起以使我的生活更轻松。我有一堆来自不同 API 源的 XML 文件。它们中有不同的项目和不同数量的字段。它们的共同点是一个共同的字段,例如“clientid”。

我想要做的是最终得到一个 CSV,其中包含所有 XML 的组合标头及其相应数据。所以我需要能够确保来自 12345 的“clientid”的所有信息都添加到“itemid”拉取中同名的客户端 id 的行尾

项目数据:

<item>
<id>99899</id>
<client-id>12345</client-id>

部分脚本:

def parseXML():
    ### Parse XML and convert to CSV ###
    #Get XML Source #
    tree = ET.fromstring(getdata)
    # open a file for writing
    xmlTest01 = open('xmlTest01.csv', 'w')
    # create the csv writer object
    csvwriter = csv.writer(xmlTest01)
    item_head = []

    count = 0
    for member in tree.findall('item'):
                    item = []
                    if count == 0:
                                    id = member.find('id').tag
                                    item_head.append(id)
                                    clientid = member.find('client-id').tag
                                    item_head.append(clientid)

                    id = member.find('id').text
                    item.append(id)
                    clientid = member.find('client-id').text
                    item.append(clientid)
                    csvwriter.writerow(item)
    xmlTest01.close()

下一组数据里面有这个:

<client>
<id>12345</id>
<name>Clients name</name>
<current type="boolean">true</current>
<status>good</status>

所以我想检查上一组数据中的行是否有相同的 clientid,然后将名称、当前和状态添加到该行的末尾。

关于最佳方式的任何想法?我有大约 5-7 个此类文件要合并。在将文件转换为 CSV 之前,我是否应该先尝试合并文件?如果他们都有相似的内容,这可能没问题,但他们没有。

结合两个 xml 文件的值的所需输出:

id,clientid,name,current,status
99899,12345,Clients name,true,good

【问题讨论】:

  • 你能发布一个更完整的输入 XML(带有根标签)而不是一个reproducible example 的 sn-ps 吗?所需的 csv 输出结果可能会有所帮助并说明您的文字描述。
  • @Parfait 好的。我将尝试尽快简化它,并使用更简化的工作示例更新问题。我无法按原样发布的真实数据。所以我需要全部修改,并且一些 xml 文件非常大。第一个的根标签只是 而第二个是 然后它只是我放在那里的几百个不同的 id 和字段内容。我正在为此提取实时数据,因此我需要制作一个示例文件以在此处发布一个使用它们的工作脚本。它现在一次只能使用一个 xml。但我不知道如何结合

标签: python xml csv merge


【解决方案1】:

考虑遍历这三个文件并有条件地检查客户端 ID。将 xml 值解析为您写入 csv 文件的列表:

import csv
import xml.etree.ElementTree as ET 

def parseXML():
    projecttree = ET.parse('projects.xml')
    clienttree = ET.parse('clients.xml')
    teamtasktree = ET.parse('teammembers.xml')

    projectroot = projecttree.getroot()
    clientroot = clienttree.getroot()
    teamtaskroot = teamtasktree.getroot()

    data = []
    for i in projectroot.iter('project'):
        for j in clientroot.iter('client'):
            clientid = i.find('client-id').text
            if clientid == j.find('id').text:
                data.append(i.find('id').text)
                data.append(j.find('id').text)
                data.append(j.find('name').text)
                data.append(j.find('active').text)
                data.append(i.find('name').text)
                data.append(i.find('active').text)
                data.append(i.find('billable').text)
                data.append(i.find('bill-by').text)
                data.append(i.find('hourly-rate').text)
                data.append(i.find('budget').text)
                data.append(i.find('over-budget-notification-percentage').text)
                data.append(i.find('created-at').text)
                data.append(i.find('updated-at').text)
                data.append(i.find('starts-on').text)
                data.append(i.find('ends-on').text)
                data.append(i.find('estimate').text)
                data.append(i.find('estimate-by').text)
                data.append(i.find('notes').text)
                data.append(i.find('cost-budget').text)

        cnt = 1     
        for tm in teamtaskroot.iter('team_members'):
            for item in tm.iter('item'):                
                if item.find('cid').text == clientid and cnt <= 3:
                    data.append(item.find('full_name').text)
                    data.append(item.find('cost_rate').text)
                cnt += 1

        cnt = 1
        for tk in teamtaskroot.iter('tasks'):
            for item in tk.iter('item'):                
                if item.find('cid').text == clientid and cnt <= 2:
                    data.append(item.find('task_id').text)
                    data.append(item.find('total_hours').text)
                cnt += 1

    with open('Output.csv', 'w') as f:
        csvwriter = csv.writer(f, lineterminator = '\n')
        csvwriter.writerow(['Pid', 'Clientid', 'ClientName', 'ClientActive', 'ProjectName', 'ProjectActive',
                            'Billable', 'BillBy', 'HourlyRate', 'Budget', 'OverbudgetNotificationPercentage',
                            'CreatedAt', 'UpdatedAt', 'StartsOn', 'EndsOn', 'Estimate', 'EstimateBy',
                            'Notes', 'CostBudget', 'TeammemberName1', 'CostRate1', 'TeammemberName2', 'CostRate2',
                            'TeammemberName3', 'CostRate3', 'TaskId1', 'TotalHours1', 'TaskId2', 'TotalHours2'])
        csvwriter.writerow(data)

if __name__ == "__main__":
    parseXML()

输出

Pid,Clientid,ClientName,ClientActive,ProjectName,ProjectActive,Billable,
BillBy,HourlyRate,Budget,OverbudgetNotificationPercentage,CreatedAt,
UpdatedAt,StartsOn,EndsOn,Estimate,EstimateBy,Notes,CostBudget,TeammemberName
1,CostRate1,TeammemberName2,CostRate2,TeammemberName3,CostRate3,
TaskId1,TotalHours1,TaskId2,TotalHours2
11493770,4708336,AFB,true,Services - Consulting - AH,true,true,Project,
421.28,16.0,80.0,2016-08-16T03:22:51Z,
2016-08-16T03:22:51Z,,,16.0,project,Random 
notes,,BobR,76.0,BobR,76.0,BobR,76.0,6357137,0.0,6357138,0.0

【讨论】:

  • 嘿...我早点睡着了。抱歉迟到了。我想这就是我真正想要的!让我尝试整合它。将报告回来。到目前为止谢谢!
  • 它比我原来的路径工作得好,更干净。我可以从这里建造。谢啦。真的很感激!
  • 很高兴听到!很高兴我能帮上忙。
  • 不想再打扰您了,先生。您帮助我将其他大型 XML 连接在一起。对于该 XML 中的每个条目,我将注入客户端 ID 和项目 ID。在这里,您向我展示了如何关联来自两个来源的 id,然后将来自每个来源的字段拉入单个 CSV,从而帮助您。你有没有机会使用第三个资源来更新它?我尝试按照您的示例进行操作,但是当我添加第三个来源时,我得到了空白的 CSV。我不确定是否是因为在第三个来源中相同的客户端 ID 有多个匹配项。这是这个脚本我需要的最后一点。
  • 您想要的 csv 数据文件是什么?具体来说,您需要从所有三个来源中捕获哪些字段?每个 id 是否应该只有一行,因为 team_members 和任务 xml 每个 id 有多个元素?
【解决方案2】:

此外,请考虑XSLT,这是一种特殊用途的转换语言,它可以直接将 XML 转换为 CSV,甚至可以使用其 document() 函数从其他 XML 文件进行解析。 Python 的lxml 模块可以处理XSLT 1.0 脚本。确保所有三个 xml 都位于同一目录中。

XSLT 脚本(另存为 .xsl 文件 -- 一个特殊的 .xml 文件 -- 在下面的 Python 中调用)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output version="1.0" encoding="UTF-8" method="text" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>

  <xsl:template match="/projects">
    <xsl:copy>
      <xsl:text>Pid,Clientid,ClientName,ClientActive,ProjectName,ProjectActive,Billable,BillBy,HourlyRate,</xsl:text>
      <xsl:text>Budget,OverbudgetNotificationPercentage,CreatedAt,UpdatedAt,StartsOn,EndsOn,Estimate,EstimateBy,</xsl:text>
      <xsl:text>Notes,CostBudget,TeammemberName1,CostRate1,TeammemberName2,CostRate2,TeammemberName3,CostRate3,</xsl:text>
      <xsl:text>TaskId1,TotalHours1,TaskId2,TotalHours2&#xa;</xsl:text>      
      <xsl:apply-templates select="project"/>      
    </xsl:copy>
  </xsl:template>

  <xsl:template match="project">    
    <xsl:variable name="clientid" select="client-id"/>
    <xsl:value-of select="concat(id, ',')"/>
    <xsl:variable name="delimiter"><xsl:text>&quot;,&quot;</xsl:text></xsl:variable>

    <xsl:for-each select="document('clients.xml')/clients/client[id=$clientid]/*
                                   [local-name()='id' or local-name()='name' or local-name()='active']">
      <xsl:value-of select="." />
      <xsl:if test="position() != last()">
        <xsl:text>,</xsl:text>
      </xsl:if>      
    </xsl:for-each>

    <xsl:value-of select="concat(',',name,',',active,',',billable,',',bill-by,',',hourly-rate,',',budget,',',
                                 over-budget-notification-percentage,',',created-at,',',updated-at,',',starts-on,',',ends-on,',',
                                 estimate,',',estimate-by,',',notes,',',cost-budget,',')"/>        

    <xsl:for-each select="document('teammembers.xml')/root/team_members/item[cid=$clientid]/*
                                   [local-name()='full_name' or local-name()='cost_rate']">
      <xsl:if test="position() &lt; 5">
        <xsl:value-of select="." />      
        <xsl:text>,</xsl:text>
      </xsl:if>
    </xsl:for-each>

    <xsl:for-each select="document('ClientItems_teammembers.xml')/root/tasks/item[cid=$clientid]/*
                                   [local-name()='task_id' or local-name()='total_hours']">
      <xsl:if test="position() &lt; 5">
        <xsl:value-of select="." />
        <xsl:if test="position() != last()">
          <xsl:text>,</xsl:text>
        </xsl:if>
      </xsl:if>
    </xsl:for-each>

    <xsl:text>&#xa;</xsl:text>
  </xsl:template>

</xsl:transform>

Python脚本(转换projects.xml并在XSLT中读取其他两个)

import lxml.etree as ET

def transformXML():
    dom = ET.parse('projects.xml')
    xslt = ET.parse('XSLTscript.xsl')

    transform = ET.XSLT(xslt)
    newdom = transform(dom)

    with open('Output.csv'),'w') as f:
        f.write(str(newdom))

if __name__ == "__main__":
    transformXML()

输出

Pid,Clientid,ClientName,ClientActive,ProjectName,ProjectActive,Billable,
BillBy,HourlyRate,Budget,OverbudgetNotificationPercentage,CreatedAt,
UpdatedAt,StartsOn,EndsOn,Estimate,EstimateBy,Notes,CostBudget,TeammemberName
1,CostRate1,TeammemberName2,CostRate2,TeammemberName3,CostRate3,
TaskId1,TotalHours1,TaskId2,TotalHours2
11493770,4708336,AFB,true,Services - Consulting - AH,true,true,Project,
421.28,16.0,80.0,2016-08-16T03:22:51Z,
2016-08-16T03:22:51Z,,,16.0,project,Random 
notes,,BobR,76.0,BobR,76.0,BobR,76.0,6357137,0.0,6357138,0.0,

【讨论】:

  • 老兄!!你真棒。多年来,我花了很多时间帮助虚幻游戏开发社区中的人们,这是 soeone 第一次花费大量时间来帮助我。我想送你一只小狗什么的!现在测试第一个选项。记录第二个以供以后研究。对于第一个选项,如果确实在 CSV 中说 10 个任务和 5 个团队成员,但在循环中它会遇到一个只有 3 个任务和一个或没有团队成员的情况。剩下的会是空白吗?还是会出错?我很快就会发现,哈哈。谢谢!将报告结果。
  • 它几乎完美运行!我切换到 fromstring 因为我在内存中进行所有获取,而不是将其保存为文件。但是,是的,如果任务编号或团队成员项目与字段“task1”“task2”等的数量不匹配。它开始在同一行中写入下一行,所以我最终得到了大量的第一行而没有其他行。
  • 我假设它的计数....
  • 您使用的是哪个脚本版本?你能用不匹配字段数量的例子更新文件吗?
  • 更新了我正在使用的实际数据。不过,我并没有从文件中提取,当我运行脚本时,这一切都在内存中。我认为这并不重要,但是。