<p>I'm currently trying ElementTree and it looks fine, it escapes HTML entities and so on and so forth. Am I missing something truly wonderful I haven't heard of?</p>

<p>This is similar to what I'm actually doing:</p>

<pre><code>import xml.etree.ElementTree as ET
root = ET.Element('html')
head = ET.SubElement(root,'head')
script = ET.SubElement(head,'script')
script.set('type','text/javascript')
script.text = "var a = 'I love &amp;aacute; letters'"
body = ET.SubElement(root,'body')
h1 = ET.SubElement(body,'h1')
h1.text = "And I like the fact that 3 &gt; 1"
tree = ET.ElementTree(root)
tree.write('foo.xhtml')

# more foo.xhtml
&lt;html&gt;&lt;head&gt;&lt;script type="text/javascript"&gt;var a = 'I love &amp;amp;aacute;
letters'&lt;/script&gt;&lt;/head&gt;&lt;body&gt;&lt;h1&gt;And I like the fact that 3 &amp;gt; 1&lt;/h1&gt;
&lt;/body&gt;&lt;/html&gt;
</code></pre>

[{'answer_id': 56262, 'author': 'Daren Thomas', 'author_id': 2260, 'author_profile': 'https://Stackoverflow.com/users/2260', 'pm_score': 2, 'selected': False, 'text': '<p>don\'t you actually want something like:</p>\n\n<pre><code>html(head(script(type=\'text/javascript\', content=\'var a = ...\')),\nbody(h1(\'And I like the fact that 3 &lt; 1\'), p(\'just some paragraph\'))\n</code></pre>\n\n<p>I think I saw something like that somewhere. This would be wonderful.</p>\n\n<p><strong>EDIT:</strong> Actually, I went and wrote a library today to do <em>just that</em>: <a href="http://pypi.python.org/pypi/magictree" rel="nofollow noreferrer">magictree</a></p>\n\n<p>You can use it like this:</p>\n\n<pre><code>from magictree import html, head, script, body, h1, p\nroot = html(\n         head(\n           script(\'\'\'var a = \'I love &amp;amp;aacute; letters\'\'\', \n                  type=\'text/javascript\')),\n         body(\n           h1(\'And I like the fact that 3 &gt; 1\')))\n\n# root is a plain Element object, like those created with ET.Element...\n# so you can write it out using ElementTree :)\ntree = ET.ElementTree(root)\ntree.write(\'foo.xhtml\')\n</code></pre>\n\n<p>The magic in <code>magictree</code> lies in how the importing works: The <code>Element</code> factories are created when needed. Have a <a href="http://code.google.com/p/pymagictree/source/browse/trunk/magictree.py" rel="nofollow noreferrer">look at the source</a>, it is <a href="https://stackoverflow.com/questions/2447353/getattr-on-a-module">based on an answer to another StackOverflow question</a>.</p>\n'}, {'answer_id': 56269, 'author': 'Peter Hoffmann', 'author_id': 720, 'author_profile': 'https://Stackoverflow.com/users/720', 'pm_score': 5, 'selected': False, 'text': '<p>Another way is using the <a href="http://codespeak.net/lxml/tutorial.html#the-e-factory" rel="nofollow noreferrer">E Factory</a> builder from lxml (available in <a href="http://effbot.org/zone/element-builder.htm" rel="nofollow noreferrer">Elementtree</a> too)</p>\n\n<pre><code>&gt;&gt;&gt; from lxml import etree\n\n&gt;&gt;&gt; from lxml.builder import E\n\n&gt;&gt;&gt; def CLASS(*args): # class is a reserved word in Python\n...     return {"class":\' \'.join(args)}\n\n&gt;&gt;&gt; html = page = (\n...   E.html(       # create an Element called "html"\n...     E.head(\n...       E.title("This is a sample document")\n...     ),\n...     E.body(\n...       E.h1("Hello!", CLASS("title")),\n...       E.p("This is a paragraph with ", E.b("bold"), " text in it!"),\n...       E.p("This is another paragraph, with a", "\\n      ",\n...         E.a("link", href="http://www.python.org"), "."),\n...       E.p("Here are some reserved characters: &lt;spam&amp;egg&gt;."),\n...       etree.XML("&lt;p&gt;And finally an embedded XHTML fragment.&lt;/p&gt;"),\n...     )\n...   )\n... )\n\n&gt;&gt;&gt; print(etree.tostring(page, pretty_print=True))\n&lt;html&gt;\n  &lt;head&gt;\n    &lt;title&gt;This is a sample document&lt;/title&gt;\n  &lt;/head&gt;\n  &lt;body&gt;\n    &lt;h1 class="title"&gt;Hello!&lt;/h1&gt;\n    &lt;p&gt;This is a paragraph with &lt;b&gt;bold&lt;/b&gt; text in it!&lt;/p&gt;\n    &lt;p&gt;This is another paragraph, with a\n      &lt;a href="http://www.python.org"&gt;link&lt;/a&gt;.&lt;/p&gt;\n    &lt;p&gt;Here are some reservered characters: &amp;lt;spam&amp;amp;egg&amp;gt;.&lt;/p&gt;\n    &lt;p&gt;And finally an embedded XHTML fragment.&lt;/p&gt;\n  &lt;/body&gt;\n&lt;/html&gt;\n</code></pre>\n'}, {'answer_id': 56470, 'author': 'Eli Courtwright', 'author_id': 1694, 'author_profile': 'https://Stackoverflow.com/users/1694', 'pm_score': 4, 'selected': True, 'text': '<p>I assume that you\'re actually creating an XML DOM tree, because you want to validate that what goes into this file is valid XML, since otherwise you\'d just write a static string to a file.  If validating your output is indeed your goal, then I\'d suggest</p>\n\n<pre><code>from xml.dom.minidom import parseString\n\ndoc = parseString("""&lt;html&gt;\n    &lt;head&gt;\n        &lt;script type="text/javascript"&gt;\n            var a = \'I love &amp;amp;aacute; letters\'\n        &lt;/script&gt;\n    &lt;/head&gt;\n    &lt;body&gt;\n        &lt;h1&gt;And I like the fact that 3 &amp;gt; 1&lt;/h1&gt;\n    &lt;/body&gt;\n    &lt;/html&gt;""")\n\nwith open("foo.xhtml", "w") as f:\n    f.write( doc.toxml() )\n</code></pre>\n\n<p>This lets you just write the XML you want to output, validate that it\'s correct (since parseString will raise an exception if it\'s invalid) and have your code look much nicer.</p>\n\n<p>Presumably you\'re not just writing the same static XML every time and want some substitution.  In this case I\'d have lines like</p>\n\n<pre><code>var a = \'%(message)s\'\n</code></pre>\n\n<p>and then use the % operator to do the substitution, like</p>\n\n<pre><code>&lt;/html&gt;""" % {"message": "I love &amp;amp;aacute; letters"})\n</code></pre>\n'}, {'answer_id': 58460, 'author': 'DaveP', 'author_id': 3577, 'author_profile': 'https://Stackoverflow.com/users/3577', 'pm_score': 0, 'selected': False, 'text': '<p>Try <a href="http://uche.ogbuji.net/tech/4suite/amara" rel="nofollow noreferrer">http://uche.ogbuji.net/tech/4suite/amara</a>. It is quite complete and has a straight forward set of access tools. Normal Unicode support, etc. </p>\n\n<pre><code>#\n#Output the XML entry\n#\ndef genFileOLD(out,label,term,idval):\n    filename=entryTime() + ".html"\n    writer=MarkupWriter(out, indent=u"yes")\n    writer.startDocument()\n    #Test element and attribute writing\n    ans=namespace=u\'http://www.w3.org/2005/Atom\'\n    xns=namespace=u\'http://www.w3.org/1999/xhtml\'\n    writer.startElement(u\'entry\',\n       ans,\n       extraNss={u\'x\':u\'http://www.w3.org/1999/xhtml\' ,\n                 u\'dc\':u\'http://purl.org/dc/elements/1.1\'})\n    #u\'a\':u\'http://www.w3.org/2005/Atom\',\n    #writer.attribute(u\'xml:lang\',unicode("en-UK"))\n\n    writer.simpleElement(u\'title\',ans,content=unicode(label))\n    #writer.simpleElement(u\'a:subtitle\',ans,content=u\' \')\n    id=unicode("http://www.dpawson.co.uk/nodesets/"+afn.split(".")[0])\n    writer.simpleElement(u\'id\',ans,content=id)\n    writer.simpleElement(u\'updated\',ans,content=unicode(dtime()))\n    writer.startElement(u\'author\',ans)\n    writer.simpleElement(u\'name\',ans,content=u\'Dave \')\n    writer.simpleElement(u\'uri\',ans,\n      content=u\'http://www.dpawson.co.uk/nodesets/\'+afn+".xml")\n    writer.endElement(u\'author\')\n    writer.startElement(u\'category\', ans)\n    if (prompt):\n        label=unicode(raw_input("Enter label "))\n    writer.attribute(u\'label\',unicode(label))\n    if (prompt):\n        term = unicode(raw_input("Enter term to use "))\n    writer.attribute(u\'term\', unicode(term))\n    writer.endElement(u\'category\')\n    writer.simpleElement(u\'rights\',ans,content=u\'\\u00A9 Dave 2005-2008\')\n    writer.startElement(u\'link\',ans)\n    writer.attribute(u\'href\',\n         unicode("http://www.dpawson.co.uk/nodesets/entries/"+afn+".html"))\n    writer.attribute(u\'rel\',unicode("alternate"))\n    writer.endElement(u\'link\')\n    writer.startElement(u\'published\', ans)\n    dt=dtime()\n    dtu=unicode(dt)\n    writer.text(dtu)\n    writer.endElement(u\'published\')\n    writer.simpleElement(u\'summary\',ans,content=unicode(label))\n    writer.startElement(u\'content\',ans)\n    writer.attribute(u\'type\',unicode("xhtml"))\n    writer.startElement(u\'div\',xns)\n    writer.simpleElement(u\'h3\',xns,content=unicode(label))\n    writer.endElement(u\'div\')\n    writer.endElement(u\'content\')\n    writer.endElement(u\'entry\')\n</code></pre>\n'}, {'answer_id': 62157, 'author': 'Vinko Vrsalovic', 'author_id': 5190, 'author_profile': 'https://Stackoverflow.com/users/5190', 'pm_score': 2, 'selected': False, 'text': '<p>I ended up using saxutils.escape(str) to generate valid XML strings and then validating it with Eli\'s approach to be sure I didn\'t miss any tag</p>\n\n<pre><code>from xml.sax import saxutils\nfrom xml.dom.minidom import parseString\nfrom xml.parsers.expat import ExpatError\n\nxml = \'\'\'&lt;?xml version="1.0" encoding="%s"?&gt;\\n\n&lt;contents title="%s" crawl_date="%s" in_text_date="%s" \nurl="%s"&gt;\\n&lt;main_post&gt;%s&lt;/main_post&gt;\\n&lt;/contents&gt;\'\'\' %\n(self.encoding, saxutils.escape(title), saxutils.escape(time), \nsaxutils.escape(date), saxutils.escape(url), saxutils.escape(contents))\ntry:\n    minidoc = parseString(xml)\ncatch ExpatError:\n    print "Invalid xml"\n</code></pre>\n'}, {'answer_id': 3098902, 'author': 'oasisbob', 'author_id': 335903, 'author_profile': 'https://Stackoverflow.com/users/335903', 'pm_score': 5, 'selected': False, 'text': '<p>There\'s always <a href="http://effbot.org/zone/xml-writer.htm" rel="noreferrer">SimpleXMLWriter</a>, part of the ElementTree toolkit. The interface is dead simple.</p>\n\n<p>Here\'s an example: </p>\n\n<pre><code>from elementtree.SimpleXMLWriter import XMLWriter\nimport sys\n\nw = XMLWriter(sys.stdout)\nhtml = w.start("html")\n\nw.start("head")\nw.element("title", "my document")\nw.element("meta", name="generator", value="my application 1.0")\nw.end()\n\nw.start("body")\nw.element("h1", "this is a heading")\nw.element("p", "this is a paragraph")\n\nw.start("p")\nw.data("this is ")\nw.element("b", "bold")\nw.data(" and ")\nw.element("i", "italic")\nw.data(".")\nw.end("p")\n\nw.close(html)\n</code></pre>\n'}, {'answer_id': 7060046, 'author': 'Mikhail Korobov', 'author_id': 114795, 'author_profile': 'https://Stackoverflow.com/users/114795', 'pm_score': 3, 'selected': False, 'text': '<p><a href="https://github.com/galvez/xmlwitch">https://github.com/galvez/xmlwitch</a>:</p>\n\n<pre><code>import xmlwitch\nxml = xmlwitch.Builder(version=\'1.0\', encoding=\'utf-8\')\nwith xml.feed(xmlns=\'http://www.w3.org/2005/Atom\'):\n    xml.title(\'Example Feed\')\n    xml.updated(\'2003-12-13T18:30:02Z\')\n    with xml.author:\n        xml.name(\'John Doe\')\n    xml.id(\'urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6\')\n    with xml.entry:\n        xml.title(\'Atom-Powered Robots Run Amok\')\n        xml.id(\'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a\')\n        xml.updated(\'2003-12-13T18:30:02Z\')\n        xml.summary(\'Some text.\')\nprint(xml)\n</code></pre>\n'}, {'answer_id': 19728898, 'author': 'Nick Johnson', 'author_id': 12030, 'author_profile': 'https://Stackoverflow.com/users/12030', 'pm_score': 2, 'selected': False, 'text': '<p>For anyone encountering this now, there\'s actually a way to do this hidden away in Python\'s standard library in <a href="http://docs.python.org/2.7/library/xml.sax.utils.html" rel="nofollow">xml.sax.utils.XMLGenerator</a>. Here\'s an example of it in action:</p>\n\n<pre><code>&gt;&gt;&gt; from xml.sax.saxutils import XMLGenerator\n&gt;&gt;&gt; import StringIO\n&gt;&gt;&gt; w = XMLGenerator(out, \'utf-8\')\n&gt;&gt;&gt; w.startDocument()\n&gt;&gt;&gt; w.startElement("test", {\'bar\': \'baz\'})\n&gt;&gt;&gt; w.characters("Foo")\n&gt;&gt;&gt; w.endElement("test")\n&gt;&gt;&gt; w.endDocument()\n&gt;&gt;&gt; print out.getvalue()\n&lt;?xml version="1.0" encoding="utf-8"?&gt;\n&lt;test bar="baz"&gt;Foo&lt;/test&gt;\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/56229', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/5190/']