Python元素树赢得';在使用UTF-8进行输出时,不能转换非中断空格

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 &lt;</p> <

我正在尝试使用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 &lt;</p>
<p>Non-breaking space &nbsp;</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 &lt;</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>
    &lt;htm&gt;
    &lt;body&gt;
    &lt;p&gt;Less than &amp;lt;&lt;/p&gt;
    &lt;p&gt;Non-breaking space &amp;nbsp;&lt;/p&gt;
    &lt;/body&gt;
    &lt;/html&gt;
    </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 &lt;</p>
    <p>Non-breaking space &nbsp;</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")