解析XML文件时出现UnicodeEncodeError(ElementTree)/ ValueError(lxml)

5 投票
3 回答
7287 浏览
提问于 2025-04-17 20:15

我向CareerBuilder API发送了一个GET请求:

import requests

url = "http://api.careerbuilder.com/v1/jobsearch"
payload = {'DeveloperKey': 'MY_DEVLOPER_KEY',
           'JobTitle': 'Biologist'}
r = requests.get(url, params=payload)
xml = r.text

然后我收到了一个看起来像这样的XML。不过,我在解析这个XML时遇到了问题。

我尝试使用了lxml

>>> from lxml import etree
>>> print etree.fromstring(xml)

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    print etree.fromstring(xml)
  File "lxml.etree.pyx", line 2992, in lxml.etree.fromstring (src\lxml\lxml.etree.c:62311)
  File "parser.pxi", line 1585, in lxml.etree._parseMemoryDocument (src\lxml\lxml.etree.c:91625)
ValueError: Unicode strings with encoding declaration are not supported.

或者ElementTree:

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    print ET.fromstring(xml)
  File "C:\Python27\lib\xml\etree\ElementTree.py", line 1301, in XML
    parser.feed(text)
  File "C:\Python27\lib\xml\etree\ElementTree.py", line 1641, in feed
    self._parser.Parse(data, 0)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 3717: ordinal not in range(128)

所以,尽管这个XML文件是从

<?xml version="1.0" encoding="UTF-8"?>

开始的,我觉得里面可能有一些不被允许的字符。我该如何用lxmlElementTree来解析这个文件呢?

3 个回答

0

我知道这个问题已经有答案了,但我在使用Python3时遇到了类似的问题,而在Python2上却没问题。我的解决办法是:先用 str_xml.encode() 进行编码,然后再用 xml = etree.fromstring(str_xml) 来解析,接着就可以进行标签和属性的提取了。

5

更正!

下面是我之前的错误。简单来说,当我们使用 .text 方法时,得到的结果是一个 Unicode 编码的字符串。使用这个方法会在 lxml 中引发以下异常:

ValueError: 带有编码声明的 Unicode 字符串不被支持。请使用字节输入或没有声明的 XML 片段。

这基本上意味着 @martijn-pieters 是对的,我们必须使用 .content 返回的原始响应。

错误的回答(但可能对某些人有趣)

对于感兴趣的人来说。我认为这个错误发生的原因可能是 requests 做了一个错误的猜测,具体解释可以参考 Response.text 的文档

响应的内容,以 Unicode 格式。

如果 Response.encoding 是 None,编码将通过 chardet 来猜测。

响应内容的编码完全基于 HTTP 头部来决定,严格遵循 RFC 2616。如果你能利用非 HTTP 的知识来更好地猜测编码,应该在访问这个属性之前适当地设置 r.encoding。

所以,按照这个说法,我们也可以通过明确设置编码 r.encoding = 'UTF-8' 来确保 requests 的 r.text 正确编码响应内容。

这种方法在用 lxml 解析之前,增加了一个验证,确保接收到的响应确实是正确编码的。

18

你现在使用的是解码过的 Unicode 值。建议你使用r.raw 原始响应数据

r = requests.get(url, params=payload, stream=True)
r.raw.decode_content = True
etree.parse(r.raw)

这样可以直接从响应中读取数据;记得在调用.get()时加上stream=True这个选项。

设置r.raw.decode_content = True这个标志,可以确保即使响应是经过 gzip 或 deflate 压缩的,原始的 socket 也会给你解压后的内容。

你并不一定要流式处理响应;对于较小的 XML 文档,使用response.content 属性也是可以的,这个属性是未解码的响应主体:

r = requests.get(url, params=payload)
xml = etree.fromstring(r.content)

XML 解析器总是期望输入的是字节,因为 XML 格式本身规定了解析器如何将这些字节解码为 Unicode 文本。

撰写回答