Python怎么用lxml验证XML是否符合DTD规范

lxml可通过启用DTD加载和验证实现XML对DTD的合规性检查,需设置XMLParser(dtd_validation=True, load_dtd=True, resolve_entities=False),并捕获etree.DocumentInvalid异常来判断验证是否失败。

lxml 不直接支持用 DTD 进行严格验证(比如 `xmlschema` 那样的纯模式校验),但它可以在解析时**启用 DTD 加载和验证**,并捕获是否符合 DTD 的错误。关键在于:必须显式开启 DTD 加载、设置解析器选项,并配合 `XMLParser` 使用。

1. 确保 DTD 文件可访问且被正确引用

XML 文件开头需有正确的 DOCTYPE 声明,例如:

其中 "note.dtd" 是相对路径(或绝对路径/URL)。lxml 默认不加载外部 DTD,所以你要确保该文件存在、路径正确,且没有网络阻断(如防火墙阻止远程 DTD)。

2. 创建带 DTD 支持的解析器

使用 etree.XMLParser(dtd_validation=True),并推荐关闭外部实体以提升安全性和稳定性:

  • dtd_validation=True:启用 DTD 验证(默认为 False)
  • load_dtd=True:允许加载外部 DTD(必需)
  • resolve_entities=False:防止 XXE 攻击,同时避免因实体解析失败干扰验证

3. 解析并捕获验证错误

验证失败时,lxml 会抛出 etree.XMLSyntaxError(不是普通异常,而是子类 etree.DocumentInvalid)。建议用 try/except 捕获,并检查错误信息:

  • 成功解析 → XML 符合 DTD
  • 抛出 etree.DocumentInvalid → DTD 验证失败(如元素缺失、属性非法、顺序错误等)
  • 抛出其他 XMLSyntaxError → 可能是格式错误(如标签未闭合)、DTD 路径不对、编码问题等

4. 示例代码(简洁可用)

假设你有 data.xml 和同目录下的 note.dtd

from lxml import etree

parser = etree.XMLParser(
    dtd_validation=True,
    load_dtd=True,
    resolve_entities=False
)

try:
    tree = etree.parse("data.xml", parser)
    print("✅ XML 符合 DTD 规范")
except etree.DocumentInvalid as e:
    print("❌ DTD 验证失败:", e)
except etree.XMLSyntaxError as e:
    print("⚠️  解析失败(可能 DTD 不可访问/格式错误):", e)

基本上就这些。注意:lxml 的 DTD 验证能力有限,不支持参数实体、条件节等高级特性;若需更严格的结构校验,建议转用 XSD + xmlschema 库。