【发布时间】:2015-02-25 13:39:21
【问题描述】:
我注意到 xml 实体 " 会自动强制转换为它们真正的原始字符:
>>> from lxml import etree as et
>>> parser = et.XMLParser()
>>> xml = et.fromstring("<root><elem>"hello world"</elem></root>", parser)
>>> print et.tostring(xml, pretty_print=1)
<root>
<elem>"hello world"</elem>
</root>
>>>
我找到了一个相关的旧(2009-02-07)thread:
s = cStringIO.StringIO(""""她是男人!"""") e = etree.parse(s,etree.XMLParser(resolve_entities=False))
注意还有etree.fromstring()。
etree.tostring(e) '“她是男人!”'
我原以为 resolve_entities=False 会阻止 翻译,例如,“到”。
“resolve_entities”选项适用于在 DTD 中定义的实体 您要保留其中的引用而不是解析的值。 您提到的实体是 XML 规范的一部分,而不是 DTD。
是否有另一种方法来防止这种行为(或者,如果没有别的, 事后反转)?
嗯,你得到的是格式良好的 XML。请问你为什么需要 输出中的实体引用?
不过,响应是您想要这样做的原因,这个问题没有直接的答案。我很惊讶,因为 etree 解析器强制转换而没有提供禁用它的选项。
以下示例说明了为什么我需要此解决方案,此 xml 用于 xbmc skinning 解析器:
>>> print open("/tmp/so.xml").read() #the original file
<window id="1234">
<defaultcontrol>101</defaultcontrol>
<controls>
<control type="button" id="101">
<onfocus>Dialog.Close(212)</onfocus>
<onfocus>SetFocus(11)</onfocus>
</control>
<control type="button" id="102">
<visible>StringCompare(VideoPlayer.PlotOutline,Stream.IsPlaying) + !Skin.HasSetting(Stream.IsUpdated)</visible>
<onfocus>RunScript(script.test)</onfocus>
<onfocus>SetFocus(11)</onfocus>
</control>
<control type="button" id="103">
<visible>SubString(VideoPlayer.PlotOutline,Video.IsPlaying)</visible>
<onfocus>Close</onfocus>
<onfocus>RunScript("/.xbmc/addons/script.hello.world/default.py","$INFO[VideoPlayer.Album]","$INFO[VideoPlayer.Genre]")</onfocus>
</control>
</controls>
</window>
>>> root = et.parse("/tmp/so.xml", parser)
>>> r = root.getroot()
>>> for c in r:
... for cc in c:
... if cc.attrib.get('id') == "103":
... cc.remove(cc[1]) #remove 1 element, it's just a demonstrate
...
>>> o = open("/tmp/so.xml", "w")
>>> o.write(et.tostring(r, pretty_print=1)) #save it back
>>> o.close()
>>> print open("/tmp/so.xml").read() #the file after implemented
<window id="1234">
<defaultcontrol>101</defaultcontrol>
<controls>
<control type="button" id="101">
<onfocus>Dialog.Close(212)</onfocus>
<onfocus>SetFocus(11)</onfocus>
</control>
<control type="button" id="102">
<visible>StringCompare(VideoPlayer.PlotOutline,Stream.IsPlaying) + !Skin.HasSetting(Stream.IsUpdated)</visible>
<onfocus>RunScript(script.test)</onfocus>
<onfocus>SetFocus(11)</onfocus>
</control>
<control type="button" id="103">
<visible>SubString(VideoPlayer.PlotOutline,Video.IsPlaying)</visible>
<onfocus>RunScript("/.xbmc/addons/script.hello.world/default.py","$INFO[VideoPlayer.Album]","$INFO[VideoPlayer.Genre]")</onfocus>
</control>
</controls>
</window>
>>>
正如您在最后的 id "103" 下的 onfocus 元素中看到的," 不再是原来的形式,如果"$INFO[VideoPlayer.Album]" 变量包含嵌套引号并变为无效和错误的 ""test""。
那么,我有什么办法可以让 " 保持其原始形式吗?
[更新]: 对于感兴趣的人,其他 3 个预定义的 xml 实体,即 gt、lt 和 amp 只能通过使用 method= 进行转换"html" 和 script 标签。无论是lxml VS xml.etree.ElementTree 还是python2 VS python3 的机制都是一样的,让人摸不着头脑:
>>> from lxml import etree as et
>>> r = et.fromstring("<root><script>"'&><</script><p>"'&><</p></root>")
>>> print et.tostring(r, pretty_print=1, method="xml")
<root>
<script>"'&><</script>
<p>"'&><</p>
</root>
>>> print et.tostring(r, pretty_print=1, method="html")
<root><script>"'&><</script><p>"'&><</p></root>
>>>
[更新2]: 以下是所有可能的 html 标签的列表:
#https://github.com/html5lib/html5lib-python/blob/master/html5lib/sanitizer.py
acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area',
'article', 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button',
'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
'figcaption', 'figure', 'footer', 'font', 'form', 'header', 'h1',
'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input', 'ins',
'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select',
'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong',
'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video']
from lxml import etree as et
for e in acceptable_elements:
r = et.fromstring(e.join(["<", ">hello&world</", ">"]))
s = et.tostring(r, pretty_print=1, method="html")
closed_tag = "</" + e + ">"
if closed_tag not in s:
print s
运行这段代码,你会看到如下输出:
<area>
<br>
<col>
<hr>
<img>
<input>
如您所见,只打印了打开标签,其余的只是进入黑洞。我测试了所有 5 个 xml 实体并且都具有相同的行为。这太令人困惑了。这在使用 HTMLParser 时没有发生,所以我猜 fromstring(method 应该默认为 xml) 和 tostring(method="html") 步骤之间存在错误。而且我发现它与实体无关,因为“hello”(没有实体)也被截断为
(而且你好无处可去,如果使用方法=“它可以随时出现xml”打印出来)。
【问题讨论】:
-
我假设某处有“”,例如id="101",因此很难确定哪些引号需要保留,哪些不需要。所以我不能简单地对整个文件或每个文本执行 .replace。
-
有道理,但它会在
>之后和<之前,对吗?对于一种 hacky 方式,我认为您需要找到一种可以安全地执行string.replace的模式,或者也许有人可能会建议一种我不知道的更好方法。祝你好运:) -
我能想到的是替换所有 2 个 xml 预定义实体,包括 &apos 和 "在解析之前到特定的字符串(例如ANDquot;),然后在解析后将其替换回所有这些字符串。这很丑陋,但它应该可以完成工作。我很惊讶其他 3 个 xml 预定义的 xml 实体(>、<、&)不会由 xml etree 自动转换。如果有人提出更好的想法,我将不胜感激。
-
从我目前注意到的情况来看,3 个 xml 预定义实体(>、<、&)只有在使用 method="html" 时才会被转换,并且标签必须命名为“脚本”。参考stackoverflow.com/questions/19017253/…
-
确实需要深入解析器源代码才能找出原因,但祝你好运。
标签: python xml xml-parsing lxml elementtree