lxml vs ElementTree

lxml versus Python's Built-in ElementTree

xml.etree.ElementTree ships with Python, uses Expat 1,381 (Expat) and shares lxml 3,063 's basic API, so code moves between them; the differences are at the edges. This script runs four tests through both, two of them hostile inputs: an external entity that reads a local file (XXE) and a "billion laughs" entity bomb:

compare.py: the same four tests on ElementTree and lxmlPython
import xml.etree.ElementTree as ET
from lxml import etree
roots = {ET: ET.parse("booknest-catalog.xml").getroot(),
         etree: etree.parse("booknest-catalog.xml").getroot()}
xxe = b'<!DOCTYPE b [<!ENTITY x SYSTEM "file:///etc/hostname">]><b>&x;</b>'
levels = b"".join(b'<!ENTITY %c "%s">' % (c, b"&%c;" % (c - 1) * 10) for c in range(98, 106))
bomb = b'<!DOCTYPE b [<!ENTITY a "' + b"x" * 100 + b'">' + levels + b"]><b>&i;</b>"
tests = {
    "XPath": lambda r, m: len(r.xpath("book[supply/price > 20]") if m is etree
                              else r.findall("book[supply/price > 20]")),
    "parent": lambda r, m: r.find("book").getparent().tag,
    "XXE": lambda r, m: m.fromstring(xxe).text,
    "entity bomb": lambda r, m: len(m.fromstring(bomb).text),
}
for test, fn in tests.items():
    for mod, name in ((ET, "ET"), (etree, "lxml")):
        try:
            result = fn(roots[mod], mod)
        except Exception as e:
            result = f"{type(e).__name__}: {str(e)[:52]}"
        print(f"{test:11} {name:4} -> {result}")
Output
XPath       ET   -> SyntaxError: invalid predicate
XPath       lxml -> 3
parent      ET   -> AttributeError: 'xml.etree.ElementTree.Element' object has no attrib
parent      lxml -> catalog
XXE         ET   -> ParseError: undefined entity &x;: line 1, column 59
XXE         lxml -> XMLSyntaxError: Entity 'x' not defined, line 1, column 63 (<string>,
entity bomb ET   -> ParseError: limit on input amplification factor (from DTD and en
entity bomb lxml -> XMLSyntaxError: Maximum entity amplification factor exceeded, see xm
ElementTree and lxml compared
Feature ElementTree lxml
Paths ElementPath subset Full XPath 1.0
Parent, source line No getparent(), sourceline
XSLT, XSD, RELAX NG No XSLT 1.0, XSD 1.0, RELAX NG
External entities Not loaded Not loaded by default

Both refused the hostile inputs: current libxml2 3,427 and Expat cap entity amplification, and neither fetches external entities by default. Older versions did, so pin current releases or use defusedxml, and never enable resolve_entities or load_dtd for outside data.