lxml.html.tostring 打印时重排序 doctype 和 xml 标签
假设我有一个文件叫做 test.html,里面有一些内容,
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>Components of the SDK</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body></body></html>
然后在 Python 的命令行里执行这个,
>>>import lxml.html
>>>t = lxml.html.parse('test.html')
>>>lxml.html.etree.tostring(t)
>>>'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n<?xml version="1.0" encoding="UTF-8" standalone="no"??><html xmlns="http://www.w3.org/1999/xhtml"><head><title>Components of the SDK</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body/></html>'
你会注意到,当 lxml 读取数据后,再通过 tostring 打印出来时,文档类型和 XML 标签的顺序被颠倒了。我们怎么才能解决这个问题,让它不去修改文档(假设文档是格式正确的)呢?
1 个回答
6
简短回答
可以这样做(假设你的文档都是格式正确的XML)
etx = lxml.etree.parse('test.html')
print lxml.etree.tostring(etx, xml_declaration=True, encoding=etx.docinfo.encoding, standalone=etx.docinfo.standalone)
解释
test.html
实际上并不是有效的HTML。它里面有空元素和一个XML处理指令,而这些HTML是无法理解的。HTML解析器把这个XML处理指令当成SGML处理指令来解析(这就像是 <? ... >
而不是XML的 <? ... ?>
),内容是 xml version="1.0" encoding="UTF-8" standalone="no"?
。所以在重新序列化为XML时,这个XML处理指令就变成了双问号,像这样: ??>
使用 html5lib
解析器或序列化器的结果稍微好一些——当重新序列化为XML时,处理指令会被放在注释里。这是因为HTML5也不允许SGML处理指令,并且会把XML的开头部分当成无用的文本来忽略。
为了得到你想要的结果,建议使用XML解析器(lxml.etree
)来解析和序列化你的文档。它看起来是格式正确的XML和有效的XHTML1.1。如果你用HTML序列化器(lxml.html.tostring()
,而不是 lxml.html.etree.tostring()
)来序列化,它会输出一个多语言的XHTML文档。
需要注意的是,序列化器并不会完全保留XML声明(毕竟这不是XML信息集的一部分)。你需要通过 docinfo
属性把这些信息传递给 tostring()
方法。