etree.tostring() 默认按 xml 规则序列化 html,导致自闭合标签错误;需用 html.fromstring() 解析并指定 method="html" 和 encoding="unicode",同时手动补全 doctype 和 meta charset。

etree.tostring() 默认输出 XML,不是 HTML
直接用 etree.tostring() 处理 HTML 片段时,它会按 XML 规则序列化:自闭合标签(如 <img alt="Python lxml etree.tostring method html 生成HTML兼容XML" >)强制写成 <img alt="Python lxml etree.tostring method html 生成HTML兼容XML" >,<br> 变成 <br>,甚至 <p></p> 里空内容也会被收成 <p></p>。浏览器能容忍,但不符合 HTML5 规范,某些前端工具或校验器会报错。
根本原因是 lxml 的 etree 模块默认走 XML 序列化路径,不识别 HTML 的“宽松标签语义”。
- 用
method="html"参数显式指定输出模式:etree.tostring(tree, method="html", encoding="unicode") - 必须配合
encoding="unicode"(或encoding=None)才能返回字符串;否则返回bytes,容易漏 decode 导致乱码或类型错误 - 如果原始树是用
html.fromstring()解析的,method="html"会自动适配 HTML 的 void 元素列表(br、img、input等),不加该参数就无效
HTML parser 和 XML parser 创建的树行为不同
很多人混用 etree.fromstring() 和 html.fromstring(),结果 tostring(..., method="html") 仍输出 XML 风格 —— 因为 etree.fromstring() 创建的是纯 XML 树,没有内置 HTML 元素知识。
html.fromstring() 返回的才是真正的 HTML 树,它内部标记了文档类型和元素分类,tostring() 才能据此决定哪些标签不该闭合。
立即学习“Python免费学习笔记(深入)”;
- 解析 HTML 字符串时,固定用
html.fromstring(html_str),别用etree.fromstring() - 如果必须用
etree模块解析(比如已知输入是 XHTML),可手动设置parser=etree.HTMLParser(),但不如直接用html模块直观 - 检查树对象类型:
isinstance(tree, html.HtmlElement)比看变量名更可靠
encoding 和 pretty_print 联动出问题
pretty_print=True 会让输出带缩进,但若同时设 encoding="utf-8",返回的是 bytes,而缩进逻辑在字节层可能破坏 UTF-8 多字节序列 —— 尤其含中文时,常见乱码或 UnicodeDecodeError。
更隐蔽的问题是:pretty_print 在 HTML 模式下对某些标签(如 <pre class="brush:php;toolbar:false;"></pre>、<script></script>)插入的换行会被浏览器渲染为实际空格,影响布局。
- 开发期调试用
encoding="unicode"+pretty_print=True最安全 - 生产环境输出 HTML,优先关掉
pretty_print,体积小、无副作用 - 真要格式化,用专门的 HTML 格式化工具(如
bs4的prettify()或外部tidy),别依赖tostring
DOCTYPE 和 meta charset 不会自动补全
etree.tostring() 只序列化树中已存在的节点,不会像浏览器那样自动注入 或 <code><meta charset="utf-8">。很多人以为加了 method="html" 就万事大吉,结果生成的片段丢进文件直接被当 XML 解析。
这不属于 tostring 的职责范围,但却是 HTML 兼容性落地的关键一环。
- 手动拼接:
"" + etree.tostring(tree, method="html", encoding="unicode") - 如果树根是
,可在解析前确保它包含<meta charset="utf-8"> - 用
html.tostring()(注意不是etree.tostring())可选doctype参数,但仅限顶层文档,且要求树结构合规











