Python:Unicode与ElementTree.parse

11 投票
3 回答
35926 浏览
提问于 2025-04-16 02:23

我正在尝试使用 Python 2.7,因为在这个版本中,Unicode 是个大问题,所以我想用 XML 文件和文本来处理它们,并使用 xml.etree.cElementTree 库来解析这些文件。但是我遇到了这个错误:

>>> import xml.etree.cElementTree as ET
>>> from io import StringIO
>>> source = """\
... <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
... <root>
...   <Parent>
...     <Child>
...       <Element>Text</Element>
...     </Child>
...   </Parent>
... </root>
... """
>>> srcbuf = StringIO(source.decode('utf-8'))
>>> doc = ET.parse(srcbuf)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 56, in parse
  File "<string>", line 35, in parse
cElementTree.ParseError: no element found: line 1, column 0

使用 io.open('filename.xml', encoding='utf-8') 传递给 ET.parse 时也会出现同样的问题:

>>> with io.open('test.xml', mode='w', encoding='utf-8') as fp:
...     fp.write(source.decode('utf-8'))
...
150L
>>> with io.open('test.xml', mode='r', encoding='utf-8') as fp:
...     fp.read()
...
u'<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n<root>\n  <Parent>\n
    <Child>\n      <Element>Text</Element>\n    </Child>\n  </Parent>\n</root>\n
'
>>> with io.open('test.xml', mode='r', encoding='utf-8') as fp:
...     ET.parse(fp)
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<string>", line 56, in parse
  File "<string>", line 35, in parse
cElementTree.ParseError: no element found: line 1, column 0

我是不是在 Unicode 和 ET 解析方面漏掉了什么?

补充说明:显然,ET 解析器和 Unicode 输入流不太兼容?下面的代码可以正常工作:

>>> with io.open('test.xml', mode='rb') as fp:
...     ET.parse(fp)
...
<ElementTree object at 0x0180BC10>

但这也意味着如果我想从内存中的文本解析,就不能使用 io.StringIO,除非我先把它编码成内存缓冲区?

3 个回答

7

我在Python 2.6中遇到了和你一样的问题。

看起来在Python 2.x和3.x版本中,cElementTree.parse的"utf-8"编码处理方式是不一样的。在Python 2.x中,我们可以使用XMLParser来处理unicode编码。例如:

import xml.etree.cElementTree as etree

parser = etree.XMLParser(encoding="utf-8")
targetTree = etree.parse( "./targetPageID.xml", parser=parser )
pageIds = targetTree.find("categorymembers")
print "pageIds:",etree.tostring(pageIds)

你可以参考这个页面了解XMLParser的方法(“XMLParser”部分):http://effbot.org/zone/elementtree-13-intro.htm

而下面的方法适用于Python 3.x版本:

import xml.etree.cElementTree as etree
import codecs

target_file = codecs.open("./targetPageID.xml",mode='r',encoding='utf-8')

targetTree = etree.parse( target_file )
pageIds = targetTree.find("categorymembers")
print "pageIds:",etree.tostring(pageIds)

希望这对你有帮助。

16

你的问题是,你给 ElementTree 提供的是 Unicode 字符串,但它其实更喜欢处理字节(bytes)。不过无论如何,它都会给你返回 Unicode。

在 Python 2.x 中,它只能处理字节。你可以告诉它这些字节用什么编码,但就这些了。所以,如果你必须处理一个代表 文本文件 的对象,比如 io.StringIO,你首先需要把它转换成其他格式。

如果你手头有一个用 UTF-8 编码的 2.x 版本的 str(也就是字节),就像你举的例子那样,可以直接用 xml.etree.cElementTree.XML 一次性解析成 XML,这样就不用担心其他问题了 :-).

如果你想处理从文件中逐步读取的数据,可以使用 xml.etree.cElementTree.parse,并配合 io.BytesIO,这样可以把数据转换成字节流,而不是字符流。如果你想用 io.open,记得加上 b 标志,这样你就能得到字节流。

在 Python 3.x 中,你可以直接把 Unicode 字符串传给 ElementTree,这样会方便一些。而且新版的 ElementTree 允许这样做也是更合理的。不过,你也不一定非得这样做,Python 3 依然接受字节作为输入。无论如何,你的起点都是字节:通过直接把字节从输入源传给 ElementTree,你可以让它在 XML 解析引擎内部智能地处理编码和解码,还能实时检测输入流中的编码声明,这在处理 XML 时可以做到,但在处理任意文本数据时就做不到了。所以,把解码的工作交给 XML 解析器来做是最合适的选择。

5

你在第一个例子里不能用

doc = ET.fromstring(source)

吗?

撰写回答