有办法强制lxml解析指定编码的Unicode字符串吗?

18 投票
4 回答
23949 浏览
提问于 2025-04-16 02:17

我有一个XML文件,它里面指定了一种编码方式。我用UnicodeDammit工具把它转换成Unicode格式(因为存储原因,我不能把它存成字符串)。后来我把这个文件传给lxml,但它不愿意忽略文件里指定的编码,还是按照原来的编码来解析,结果抛出了一个异常。

我该怎么做才能强制lxml解析这个文档呢?感觉它的这个行为太严格了。

4 个回答

2

解决方案并不是重新编码字符串。字符串内部的编码声明可能会是其他格式,而不仅仅是UTF8。不要盲目地将其重新编码为UTF8,然后期待它总是能正常工作。

正确的做法是直接去掉编码声明。你手上已经有了一个Unicode字符串,没必要再保留这个声明了!

# this is from lxml/apihelpers.pxi
RE_XML_ENCODING = re.compile(
    ur'^(<\?xml[^>]+)\s+encoding\s*=\s*["\'][^"\']*["\'](\s*\?>|)', re.U)

RE_XML_ENCODING.sub("", broken_xml_string, count=1)

在最坏的情况下(找不到XML编码声明),这里的时间复杂度是O(n),这其实挺糟糕的(但总比盲目编码成二进制要好),所以我欢迎大家提出建议。

附注:关于XML编码问题的一些有趣分析:

XML的默认编码是UTF-8还是UTF-16?

XML声明中的默认编码(UTF-8)到底有多默认?

3

基本上,解决办法就是这样做:

if isinstance(mystring, unicode):
    mystring = mystring.encode("utf-8")

说真的,lxml做得不错。

补充一下:在这个情况下,lxml错误地自动识别了编码。看起来我需要手动去查找并删除页面中的“charset”和“encoding”。

22

你不能同时从 Unicode 字符串解析数据,还在字符串里加上编码声明。所以,你要么把它变成一个编码过的字符串(因为你显然不能直接把它当字符串存储,你需要在解析之前重新编码),要么你自己用 lxml 把树序列化成 Unicode:etree.tostring(tree, encoding=unicode),而且不要加 XML 声明。你可以很容易地用 etree.fromunicode 再次解析这个结果。

详细信息可以查看这个链接:http://lxml.de/parsing.html#python-unicode-strings

补充说明:如果你已经有了 Unicode 字符串,并且无法控制它是怎么生成的,那么你需要再次编码,并告诉解析器你使用的编码:

utf8_parser = etree.XMLParser(encoding='utf-8')

def parse_from_unicode(unicode_str):
    s = unicode_str.encode('utf-8')
    return etree.fromstring(s, parser=utf8_parser)

这样可以确保,无论 XML 声明里有什么内容,解析器都会忽略,因为它总是使用 utf-8 编码。

撰写回答