Python元素树赢得';在使用UTF-8进行输出时,不能转换非中断空格
我正在尝试使用Python的ElementTree解析、操作和输出HTML:Python元素树赢得';在使用UTF-8进行输出时,不能转换非中断空格,python,xml,encoding,elementtree,Python,Xml,Encoding,Elementtree,我正在尝试使用Python的ElementTree解析、操作和输出HTML: import sys from cStringIO import StringIO from xml.etree import ElementTree as ET from htmlentitydefs import entitydefs source = StringIO("""<html> <body> <p>Less than <</p> <
import sys
from cStringIO import StringIO
from xml.etree import ElementTree as ET
from htmlentitydefs import entitydefs
source = StringIO("""<html>
<body>
<p>Less than <</p>
<p>Non-breaking space </p>
</body>
</html>""")
parser = ET.XMLParser()
parser.parser.UseForeignDTD(True)
parser.entity.update(entitydefs)
etree = ET.ElementTree()
tree = etree.parse(source, parser=parser)
for p in tree.findall('.//p'):
print ET.tostring(p, encoding='UTF-8')
导入系统
从cStringIO导入StringIO
从xml.etree导入ElementTree作为ET
从htmlentitydefs导入entitydefs
source=StringIO(“”)
少于
不间断空间
""")
parser=ET.XMLParser()
parser.parser.UseForeignDTD(True)
parser.entity.update(entitydefs)
etree=ET.ElementTree()
tree=etree.parse(源,parser=parser)
对于tree.findall('.//p')中的p:
打印ET.tostring(p,encoding='UTF-8')
在Mac OS X 10.6上使用Python 2.7运行时,我得到:
<p>Less than <</p>
Traceback (most recent call last):
File "bar.py", line 20, in <module>
print ET.tostring(p, encoding='utf-8')
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py", line 1120, in tostring
ElementTree(element).write(file, encoding, method=method)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py", line 815, in write
serialize(write, self._root, encoding, qnames, namespaces)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py", line 931, in _serialize_xml
write(_escape_cdata(text, encoding))
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py", line 1067, in _escape_cdata
return text.encode(encoding, "xmlcharrefreplace")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xa0 in position 19: ordinal not in range(128)
小于
回溯(最近一次呼叫最后一次):
文件“bar.py”,第20行,在
打印ET.tostring(p,encoding='utf-8')
tostring中的文件“/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py”,第1120行
ElementTree(element).write(文件,编码,方法=method)
写入文件“/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py”,第815行
序列化(写入、自根、编码、qnames、命名空间)
文件“/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py”,第931行,在xml中序列化
写入(\u转义\u cdata(文本、编码))
文件“/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xml/etree/ElementTree.py”,第1067行,在_escape\u cdata中
返回text.encode(编码,“xmlcharrefreplace”)
UnicodeDecodeError:“ascii”编解码器无法解码位置19中的字节0xa0:序号不在范围内(128)
我认为指定“encoding='UTF-8'”可以处理不间断的空格字符,但显然不行。我应该怎么做呢?XML只定义
,
,&apos
、“
和以及
等都来自HTML。因此,您有两种选择
您可以将源代码更改为使用数字实体,如 ;
或 ;
,两者都相当于
您可以使用定义这些值的DTD
下面是一些有用的信息(它是关于XSLT编写的,但是XSLT是使用XML编写的,所以同样适用)
现在的问题似乎包括一个堆栈跟踪;它改变了一切。你确定字符串在UTF-8
中吗?如果它解析为单字节0xA0
,那么它就不是UTF-8
,但更有可能是cp1252
或iso-8859-1
HTML与XML不同,因此
之类的标记将不可用ot工作。理想情况下,如果您试图通过XML传递该信息,您可以首先使用XML对上述数据进行编码,因此看起来像这样:
<xml>
<mydata>
<htm>
<body>
<p>Less than &lt;</p>
<p>Non-breaking space &nbsp;</p>
</body>
</html>
</mydata>
</xml>
热媒
身体
小于<;/p
pNon中断空间 ;/p
/身体
/html
然后在解析XML之后,可以对字符串进行HTML取消编码。我认为这里的问题不在于nbsp实体,而在于print语句
您的错误是:
UnicodeDecodeError:“ascii”编解码器无法解码位置19中的字节0xa0:序号不在范围内(128)
我认为这是因为您使用的是utf-8字符串(来自ET.tostring(p,encoding='utf-8')
)并尝试在ascii终端中回显。因此Python隐式地将该字符串转换为unicode,然后再次将其转换为ascii。虽然nbsp可以直接用utf-8表示,但不能直接用ascii表示。因此出现了错误
试着将输出保存到一个文件中,看看是否得到了预期的结果
或者,尝试打印ET.toString(p,encoding='ascii')
,这将导致ElementTree使用数字字符实体来表示无法用ascii表示的任何内容。0xA0是拉丁字符,而不是unicode字符,循环中p.text的值是str而不是unicode,这意味着为了在utf-8中对其进行编码,必须首先通过Python隐式转换转换为unicode字符串(即使用decode)。执行此操作时,它假定为ascii,因为它没有被告知任何其他内容。0xa0不是有效的ascii字符,但它是有效的拉丁字符
之所以使用拉丁1字符而不是unicode字符,是因为entitydefs是名称到拉丁1编码字符串的映射。您需要unicode代码点,可以从htmlentitydef.name2codepoint获得该代码点
以下版本将为您修复此问题:
import sys
from cStringIO import StringIO
from xml.etree import ElementTree as ET
from htmlentitydefs import name2codepoint
source = StringIO("""<html>
<body>
<p>Less than <</p>
<p>Non-breaking space </p>
</body>
</html>""")
parser = ET.XMLParser()
parser.parser.UseForeignDTD(True)
parser.entity.update((x, unichr(i)) for x, i in name2codepoint.iteritems())
etree = ET.ElementTree()
tree = etree.parse(source, parser=parser)
for p in tree.findall('.//p'):
print ET.tostring(p, encoding='UTF-8')
导入系统
从cStringIO导入StringIO
从xml.etree导入ElementTree作为ET
从htmlentitydefs导入name2codepoint
source=StringIO(“”)
少于
不间断空间
""")
parser=ET.XMLParser()
parser.parser.UseForeignDTD(True)
name2codepoint.iteritems()中x,i的parser.entity.update((x,unichr(i)))
etree=ET.ElementTree()
tree=etree.parse(源,parser=parser)
对于tree.findall('.//p')中的p:
打印ET.tostring(p,encoding='UTF-8')
您的
正在转换为“\xa0”,这是非中断空间的默认(ascii)编码(UTF-8编码为“\xc2\xa0”)
导致UnicodeDecodeError,因为默认编解码器ascii最多只能使用128个字符,ord('\xa0')=160。请将默认编码设置为其他编码,例如:
import sys
reload(sys) # must reload sys to use 'setdefaultencoding'
sys.setdefaultencoding('latin-1')
print '\xa0'.encode('utf-8', "xmlcharrefreplace")
应该可以解决您的问题。问题不在输入上:UseForeignDTD技巧可以很好地解决这个问题。问题在输出上:内存中的文本包含0xA0,我希望它会被ET.tostring转换为UTF-8表示形式(因为我说过“encoding=”UTF-8“)。问题不在于输入:UseForeignDTD技巧在这方面效果很好。问题在于输出:内存中的文本包含0xA0,我希望它会通过ET.tostring转换为UTF-8表示形式(因为我说了“encoding=”UTF-8“)。将输出保存到文件没有效果:如果我使用“output=open”()打开文件
import sys
reload(sys) # must reload sys to use 'setdefaultencoding'
sys.setdefaultencoding('latin-1')
print '\xa0'.encode('utf-8', "xmlcharrefreplace")