我选择了 JF Sebastian 的答案,因为我认为它是最简单的,因此也是最好的,但我正在为不想安装 Beautiful Soup 的任何人添加另一个解决方案。(另外,Beautiful Soup 树构建器将在 html5lib 1.0 中被弃用。)这个解决方案感谢 Amarghosh 的提示;我只是充实了一点。查看 html5lib,我意识到它会原生输出一个 minidom 对象,这意味着我可以使用他的toprettyxml(). 这是我想出的:
from html5lib import HTMLParser, treebuilders
from cStringIO import StringIO
def tidy_html(text):
  """Returns a well-formatted version of input HTML."""
  p = HTMLParser(tree=treebuilders.getTreeBuilder("dom"))
  dom_tree = p.parseFragment(text)
  # using cStringIO for fast string concatenation
  pretty_HTML = StringIO()
  node = dom_tree.firstChild
  while node:
    node_contents = node.toprettyxml(indent='  ')
    pretty_HTML.write(node_contents)
    node = node.nextSibling
  output = pretty_HTML.getvalue()
  pretty_HTML.close()
  return output
还有一个例子:
>>> text = """<b><i>bold, italic</b></i><div>a div</div>"""
>>> tidy_html(text)
<b>
  <i>
    bold, italic
  </i>
</b>
<div>
  a div
</div>
为什么我要遍历树的孩子,而不是直接toprettyxml()调用dom_tree?我正在处理的一些 HTML 实际上是 HTML 片段,所以它缺少<head>and<body>标记。为了处理这个问题,我使用了这个parseFragment()方法,这意味着我得到了一个 DocumentFragment 作为回报(而不是一个 Document)。不幸的是,它没有writexml()方法(toprettyxml()调用),所以我遍历子节点,它们确实有方法。