'copy one xml to an other in python using lxml

I have a following code in python

from lxml import etree

offers = etree.parse(r'prices.xml')

print("offers\n")

target = offers.xpath('//offer[./vendor/text()="Qtap"]')
length = len(target)
for i in  range(length):
    print(target[i])
    etree.ElementTree(target[i]).write('output.xml', encoding='utf-8', xml_declaration=True)

I simply read the xml file. Reading data from it using xpath and want to write all of it into an other file. There are around 2000 elements shown as lenght, But script writes only last one. Sorry I know that my question is rather stupid, but it is my first program on Python.

Dispite the accepted answer that actually worked. I am gona put the simplyfied example of the xml file. As an input I get somethiln like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<yml_catalog date="2022-02-16 18:16">
    <shop>
        <purchase_currencies>
            <currency id="USD" rate="28.3"/>
            <currency id="EUR" rate="32.18"/>
            <currency id="UAH" rate="1"/>
        </purchase_currencies>
        <categories></categories>
        <offers>
            <offer id="SD00025386">
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/9/19289.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/9/19289/19289_1.jpg</picture>
                <name>Трап ANI Plast TA1612 горизонтальний з нержавіючою решіткою 150x150</name>
                <available>true</available>
                <oldCode>19289</oldCode>
                <model>TA1612</model>
                <purchase_price>405.158</purchase_price>
                <currency>UAH</currency>
                <retail_price>691</retail_price>
                <retail_oldprice></retail_oldprice>
                <retail_currency>UAH</retail_currency>
                <outlets>
                    <outlet id="85" name="Харків" instock="1"></outlet>
                    <outlet id="86" name="Київ" instock="3"></outlet>
                    <outlet id="87" name="Україна" instock="16"></outlet>
                </outlets>
                <vendor>Ани Пласт</vendor>
                <vendorCode>SD00025386</vendorCode>
            </offer>
            <offer id="SD00025387">
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/9/19290.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/9/19290/19290_1.jpg</picture>
                <name>Трап ANI Plast TA1712 вертикальний з нержавіючою решіткою 150х150</name>
                <available>true</available>
                <oldCode>19290</oldCode>
                <model>TA1712</model>
                <purchase_price>354.843</purchase_price>
                <currency>UAH</currency>
                <retail_price>605</retail_price>
                <retail_oldprice></retail_oldprice>
                <retail_currency>UAH</retail_currency>
                <outlets>
                    <outlet id="85" name="Харків" instock="20"></outlet>
                    <outlet id="86" name="Київ" instock="3"></outlet>
                    <outlet id="87" name="Україна" instock="20"></outlet>
                </outlets>
                <vendor>Ани Пласт</vendor>
                <vendorCode>SD00025387</vendorCode>
            </offer>
<offer id="SD00022605">
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508/16508_1.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508/16508_2.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508/16508_3.jpg</picture>
                <name>Донний клапан для раковини Qtap L02 з переливом</name>
                <available>true</available>
                <oldCode>16508</oldCode>
                <model>QTL02</model>
                <purchase_price>4.635</purchase_price>
                <currency>EUR</currency>
                <retail_price>261</retail_price>
                <retail_oldprice/>
                <retail_currency>UAH</retail_currency>
                <outlets>
                    <outlet id="85" name="Харків" instock="0"/>
                    <outlet id="86" name="Київ" instock="0"/>
                    <outlet id="87" name="Україна" instock="3"/>
                </outlets>
                <vendor>Qtap</vendor>
                <vendorCode>SD00022605</vendorCode>
            </offer>
            <offer id="SD00022606">
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16509.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16509/16509_1.jpg</picture>
                <name>Донний клапан для раковини Qtap L01 з переливом</name>
                <available>true</available>
                <oldCode>16509</oldCode>
                <model>QTL01</model>
                <purchase_price>1.236</purchase_price>
                <currency>EUR</currency>
                <retail_price>70</retail_price>
                <retail_oldprice/>
                <retail_currency>UAH</retail_currency>
                <outlets>
                    <outlet id="85" name="Харків" instock="0"/>
                    <outlet id="86" name="Київ" instock="0"/>
                    <outlet id="87" name="Україна" instock="1"/>
                </outlets>
                <vendor>Qtap</vendor>
                <vendorCode>SD00022606</vendorCode>
            </offer>

And my task is to get all of the offers with vendor QTAP. So the output will be:

<offer id="SD00022605">
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508/16508_1.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508/16508_2.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16508/16508_3.jpg</picture>
                <name>Донний клапан для раковини Qtap L02 з переливом</name>
                <available>true</available>
                <oldCode>16508</oldCode>
                <model>QTL02</model>
                <purchase_price>4.635</purchase_price>
                <currency>EUR</currency>
                <retail_price>261</retail_price>
                <retail_oldprice/>
                <retail_currency>UAH</retail_currency>
                <outlets>
                    <outlet id="85" name="Харків" instock="0"/>
                    <outlet id="86" name="Київ" instock="0"/>
                    <outlet id="87" name="Україна" instock="3"/>
                </outlets>
                <vendor>Qtap</vendor>
                <vendorCode>SD00022605</vendorCode>
            </offer>
            <offer id="SD00022606">
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16509.jpg</picture>
                <picture>https://isw.b2b-sandi.com.ua/imagecache/full/1/6/16509/16509_1.jpg</picture>
                <name>Донний клапан для раковини Qtap L01 з переливом</name>
                <available>true</available>
                <oldCode>16509</oldCode>
                <model>QTL01</model>
                <purchase_price>1.236</purchase_price>
                <currency>EUR</currency>
                <retail_price>70</retail_price>
                <retail_oldprice/>
                <retail_currency>UAH</retail_currency>
                <outlets>
                    <outlet id="85" name="Харків" instock="0"/>
                    <outlet id="86" name="Київ" instock="0"/>
                    <outlet id="87" name="Україна" instock="1"/>
                </outlets>
                <vendor>Qtap</vendor>
                <vendorCode>SD00022606</vendorCode>
            </offer> 

And yes, problem was the loop that overwrited a file on every iteration



Solution 1:[1]

Since it sounds like you simply need to remove <offer> nodes in your XML, consider XPath's generalized sibling, XSLT, the special-purpose language designed to transform XML files. Python's lxml library can run XSLT 1.0 scripts.

Specifically, an identity template and empty template can remove the needed nodes (vendor!='Qtap') all without a single for loop. Below will preserve the original structure of XML with less <offer> nodes.

XSLT (save as .xsl file, a special XML file)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="utf-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <!-- IDENTITY TRANSFORM -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- EMPTY TEMPLATE TO REMOVE CONTENT -->
    <xsl:template match="offer[vendor!='Qtap']"/>
</xsl:stylesheet>

Python

import lxml.etree as lx

# PARSE XML AND XSLT
doc = lx.parse("input.xml")
style = lx.parse("style.xsl")

# CONFIGURE AND RUN TRANSFORMER
transformer = lx.XSLT(style)
result = transformer(doc)

# OUTPUT TO FILE
result.write_output("output.xml")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Parfait