使用UTF-8为输出时的Python的ElementTree不会转换非换空间(Python Elem

2019-07-29 11:03发布

我试图解析,操作,并使用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')

当我运行这个使用Python 2.7在Mac OS X 10.6,我得到:

<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)

我认为指定“编码=‘UTF-8’”会照顾非打破空间的性格,但显然事实并非如此。 我应该怎么办呢?

Answer 1:

0XA0是latin1的字符,而不是一个Unicode字符和p.text的回路中的值是一个STR和不是Unicode,这意味着,为了对其进行编码以UTF-8必须首先将被Python转换成隐含一个unicode字符串(即,使用解码)。 当它这样做,它假定ASCII因为它没有告诉其他任何东西。 0XA0不是有效的ASCII字符,但它是一个有效的latin1字符。

你有LATIN1字符,而不是Unicode字符的原因是因为的entitydefs是名称的映射为latin1编码字符串。 你需要的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')


Answer 2:

XML只定义&lt;&gt;&apos;&quot;&amp;&nbsp; 和其他来自HTML。 所以,你有两个选择。

  1. 你可以改变你的源使用数字实体,像&#160;&#xA0; 这两者都是等同于&nbsp;
  2. 您可以使用DTD定义的值。

有一些有用的信息(这是写XSLT,但是XSLT是使用XML编写的,因此同样适用)在XSLT常见问题解答 。


现在的问题似乎包括一个堆栈跟踪; 改变的东西。 你确定该字符串是UTF-8 如果它解析为单字节0xA0 ,那么它是不是UTF-8但更可能cp1252iso-8859-1



Answer 3:

&nbsp; 被转换为“\ XA0”,其为不间断空格默认(ASCII)编码(使用UTF-8编码是“\ XC2 \ XA0”。)的线

'\xa0'.encode('utf-8')

导致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")

应该解决您的问题。



Answer 4:

HTML是不一样的XML,所以标签,如&nbsp; 不管用。 理想的情况是,如果你试图通过通过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>

然后解析XML后,您可以进行HTML unencode的字符串。



Answer 5:

我想你这里的问题是不是与你NBSP实体,而是与你的打印语句。

你的错误是:

UnicodeDecodeError错误:在位置19“ASCII”编解码器不能解码字节0XA0:顺序不在范围内(128)

我想这是因为你正在做一项UTF-8字符串(从ET.tostring(p, encoding='utf-8')并试图呼应它在ASCII终端。 所以Python是隐式转换该字符串为Unicode然后再次将其转换成ASCII码。 虽然NBSP 可以直接在UTF-8来表示,它不能直接以ASCII表示。 因此,错误。

尝试输出保存到一个文件,而不是,看如果你得到你所期望的。

或者,尝试print ET.toString(p, encoding='ascii')这应该引起ElementTree的使用数字字符实体来代表任何可以不与ASCII表示。



文章来源: Python ElementTree won't convert non-breaking spaces when using UTF-8 for output