Utilise lxml, qui garde les commentaires. Pour garder aussi le header, il tu peux utiliser xml_declaration et encoding:
from lxml import etree
from io import BytesIO
# c'est juste un exemple du type de xml que tu peux gérer
# ce code là n'est pas ce qui résout ton problème
tree = etree.parse(BytesIO('''<?xml version="1.0" encoding="utf8"?>
<!-- foo -->
<root>
<a>bar</a>
</root>
'''.encode('utf8')))
# si tu mets xml_declaration à True et que tu force l'encoding, tout est gardé
xml = etree.tostring(tree, xml_declaration=True, encoding=tree.docinfo.encoding)
print(xml.decode('utf8'))
Ce qui affiche:
<?xml version='1.0' encoding='utf8'?>
<!-- foo --><root>
<a>bar</a>
</root>
Après la structure est conservée, mais pas le formatage exacte.