Python 2.7中ElementTree.parse()遇UnicodeEncodeError的问题排查与解决
首先,咱们得搞清楚这个错误的核心根源:Python 2.7的默认字符串编码是ASCII,当你处理包含非ASCII字符(比如u'\xdf',也就是德语的ß)的内容时,如果没有显式指定正确的编码规则,Python就会默认用ASCII去做编码/解码操作,自然就会触发报错。
你尝试的三种方法都失败,大概率是因为没有正确匹配XML的实际编码,或者后续操作触发了默认ASCII编码,下面咱们一步步拆解问题,给出可行的解决方案:
为什么你的三种尝试都没生效?
直接用
ElementTree().parse(StringIO(apiResult.body))apiResult.body是str类型(字节串),ElementTree在解析时会尝试将字节串解码为Unicode,但它默认会用系统的默认编码(Python2里就是ASCII),遇到非ASCII字符直接就炸锅了。指定UTF-8编码的XMLParser
如果API返回的XML实际编码不是UTF-8(比如是ISO-8859-1),强制用UTF-8解码会导致字节序列无法被正确解析;另外,ElementTree会优先读取XML声明里的encoding属性,如果你指定的parser编码和XML声明不一致,它会直接忽略你的设置,用XML声明的编码来解码。指定ISO-8859-1编码的XMLParser
若XML实际是UTF-8编码,用ISO-8859-1解码会得到乱码的Unicode字符串,后续你如果操作这些字符串(比如打印、写入文件),Python会尝试用ASCII编码输出,同样触发错误。
正确的解决步骤
第一步:确认XML的实际编码
先搞清楚API返回的XML到底用的是什么编码,有两个权威渠道可以查:
- 查看响应头的
Content-Type字段:执行print(apiResult.headers.get('Content-Type')),里面可能包含charset=ISO-8859-1或charset=UTF-8这样的编码信息 - 查看XML内容的开头:执行
print(apiResult.body[:100]),看是否有<?xml version="1.0" encoding="ISO-8859-1"?>这样的声明,这里的编码是最优先的判断依据。
第二步:正确解码字节串为Unicode
在Python2.7里,处理非ASCII字符的核心原则是:尽早把字节串(str)解码为Unicode字符串(unicode),再进行解析操作,或者确保ElementTree用正确的编码直接解析字节串。
方法1:用requests的text属性自动解码
requests的Response.text会自动根据响应头和XML声明的编码,把字节串解码为Unicode字符串,你可以直接用这个来解析:
import xml.etree.ElementTree as ElementTree # 先获取已解码的Unicode内容 unicode_xml = apiResult.text # 用fromstring解析(ElementTree.fromstring接受Unicode或UTF-8编码的字节串) tree = ElementTree.fromstring(unicode_xml.encode('utf-8'))
方法2:显式指定编码解码后解析
如果text属性的自动解码不符合预期,你可以手动用实际编码解码:
# 把'ISO-8859-1'替换成你查到的实际编码 unicode_xml = apiResult.body.decode('ISO-8859-1') tree = ElementTree.fromstring(unicode_xml.encode('utf-8'))
方法3:正确配置XMLParser解析字节串
如果你想直接解析字节串,确保XMLParser的编码和XML实际编码一致,并且XML声明的编码也要匹配(如果XML声明存在的话):
from StringIO import StringIO import xml.etree.ElementTree as ElementTree # 替换成你查到的实际编码 parser = ElementTree.XMLParser(encoding='ISO-8859-1') tree = ElementTree.parse(StringIO(apiResult.body), parser=parser)
第三步:避免后续操作触发ASCII编码错误
解析完成后,如果你要打印或写入解析后的内容,记得显式指定编码,不要依赖Python2的默认ASCII编码:
# 比如打印某个元素的文本 element_text = tree.find('.//some-element').text # 显式编码为UTF-8再打印 print(element_text.encode('utf-8')) # 写入文件时也指定编码 with open('output.xml', 'w') as f: f.write(ElementTree.tostring(tree, encoding='utf-8'))
关键提醒
Python2.7的字符串处理确实容易踩编码坑,核心原则就是:尽早解码为Unicode,晚些再编码为字节串,全程明确指定编码,不要让Python默认的ASCII编码插手。
内容的提问来源于stack exchange,提问作者Steve Cohen

