 <p>Additionally, consider <a href="https://www.w3.org/Style/XSL/" rel="nofollow">XSLT</a>, the special purpose transformation language that can directly transform XML to CSV even parsing from other XML files using its <code>document()</code> function. Python's <a href="http://lxml.de/" rel="nofollow">lxml</a> module can process XSLT 1.0 scripts. Be sure all three xmls reside in same directory.</p> <p><strong>XSLT</strong> Script <em>(save as .xsl file --a special .xml file-- to be called below in Python)</em></p> <pre><code>&lt;xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"&gt; &lt;xsl:output version="1.0" encoding="UTF-8" method="text" indent="yes" omit-xml-declaration="yes"/&gt; &lt;xsl:strip-space elements="*"/&gt; &lt;xsl:template match="/projects"&gt; &lt;xsl:copy&gt; &lt;xsl:text&gt;Pid,Clientid,ClientName,ClientActive,ProjectName,ProjectActive,Billable,BillBy,HourlyRate,&lt;/xsl:text&gt; &lt;xsl:text&gt;Budget,OverbudgetNotificationPercentage,CreatedAt,UpdatedAt,StartsOn,EndsOn,Estimate,EstimateBy,&lt;/xsl:text&gt; &lt;xsl:text&gt;Notes,CostBudget,TeammemberName1,CostRate1,TeammemberName2,CostRate2,TeammemberName3,CostRate3,&lt;/xsl:text&gt; &lt;xsl:text&gt;TaskId1,TotalHours1,TaskId2,TotalHours2&amp;#xa;&lt;/xsl:text&gt; &lt;xsl:apply-templates select="project"/&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; &lt;xsl:template match="project"&gt; &lt;xsl:variable name="clientid" select="client-id"/&gt; &lt;xsl:value-of select="concat(id, ',')"/&gt; &lt;xsl:variable name="delimiter"&gt;&lt;xsl:text&gt;&amp;quot;,&amp;quot;&lt;/xsl:text&gt;&lt;/xsl:variable&gt; &lt;xsl:for-each select="document('clients.xml')/clients/client[id=$clientid]/* [local-name()='id' or local-name()='name' or local-name()='active']"&gt; &lt;xsl:value-of select="." /&gt; &lt;xsl:if test="position() != last()"&gt; &lt;xsl:text&gt;,&lt;/xsl:text&gt; &lt;/xsl:if&gt; &lt;/xsl:for-each&gt; &lt;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,',')"/&gt; &lt;xsl:for-each select="document('teammembers.xml')/root/team_members/item[cid=$clientid]/* [local-name()='full_name' or local-name()='cost_rate']"&gt; &lt;xsl:if test="position() &amp;lt; 5"&gt; &lt;xsl:value-of select="." /&gt; &lt;xsl:text&gt;,&lt;/xsl:text&gt; &lt;/xsl:if&gt; &lt;/xsl:for-each&gt; &lt;xsl:for-each select="document('ClientItems_teammembers.xml')/root/tasks/item[cid=$clientid]/* [local-name()='task_id' or local-name()='total_hours']"&gt; &lt;xsl:if test="position() &amp;lt; 5"&gt; &lt;xsl:value-of select="." /&gt; &lt;xsl:if test="position() != last()"&gt; &lt;xsl:text&gt;,&lt;/xsl:text&gt; &lt;/xsl:if&gt; &lt;/xsl:if&gt; &lt;/xsl:for-each&gt; &lt;xsl:text&gt;&amp;#xa;&lt;/xsl:text&gt; &lt;/xsl:template&gt; &lt;/xsl:transform&gt; </code></pre> <p><strong>Python</strong> script <em>(transforming projects.xml and reading in other two in XSLT)</em></p> <pre><code>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() </code></pre> <p><strong>Output</strong></p> <pre><code>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, </code></pre>