修复Python lxml中的tostring()
lxml的 tostring()
函数在只打印文档的一部分时似乎有点问题。看看这个例子:
from lxml.html import fragment_fromstring, tostring
frag = fragment_fromstring('<p>This stuff is <em>really</em> great!')
em = frag.cssselect('em').pop(0)
print tostring(em)
我本来期待看到 <em>really</em>
,但结果却打印出了 <em>really</em> great!
,这显然是错误的。这里的 ' great !' 不是选中的 em
的一部分。这个问题不仅仅是错误,对于处理结构化的XML文档来说,这种多余的文本是很常见的,真是个麻烦。
据我了解,lxml会把当前元素后面的任何自由文本存储在元素的 .tail
属性中。查看 tostring()
的代码,我发现它调用了 ElementTree.py 中的 _write()
函数,而这个函数总是会打印出尾部文本。这在处理整个树时是正确的,但在渲染子树的最后一个元素时却不应该这样,然而它并没有做出区分。
为了正确地渲染选中的XML而不带尾部文本,我尝试从头编写一个 toxml()
函数来替代它。这个方法基本上是可行的,但在处理注释、处理指令、命名空间、编码等方面有很多特殊情况需要考虑。所以我改变了思路,现在只是利用 tostring()
,对它的输出进行后处理,去掉多余的 .tail
文本:
def toxml(e):
""" Replacement for lxml's tostring() method that doesn't add spurious
tail text. """
from lxml.etree import tostring
xml = tostring(e)
if e.tail:
xml = xml[:-len(e.tail)]
return xml
一系列基本的测试显示这个方法效果不错。
有什么批评或建议吗?
1 个回答
14
那用 xml = lxml.etree.tostring(e, with_tail=False)
这个怎么样?
from lxml.html import fragment_fromstring
from lxml.etree import tostring
frag = fragment_fromstring('<p>This stuff is <em>really</em> great!')
em = frag.cssselect('em').pop(0)
print tostring(em, with_tail=False)
看起来 with_tail
是在2.0版本中新增的;你是不是用的是旧版本?