2

当使用 lxml 渲染 XHTML 时,一切都很好,除非您碰巧使用 Firefox,它似乎无法处理以命名空间为前缀的 XHTML 元素和 javascript。虽然 Opera 能够很好地执行 javascript(这适用于 jQuery 和 MathJax),但无论 XHTML 命名空间是否有前缀(h:在我的情况下),在 Firefox 中脚本都会中止并出现奇怪的错误this.head在MathJax 的例子)。

我知道这个register_namespace函数,但它既不接受None也不""作为命名空间前缀。_namespace_map我在模块中听说过lxml.etree,但是我的 Python 抱怨这个属性不存在(版本问题?)

有没有其他方法可以删除 XHTML 命名空间的命名空间前缀?请注意str.replace,正如对另一个相关问题的回答所建议的那样,这不是我可以接受的方法,因为它不了解 XML 语义并且可能很容易搞砸生成的文档。

根据要求,您会发现两个可以使用的示例。一种带有命名空间前缀一种没有. 第一个将在 Firefox 中显示 0(错误),第二个将显示 1(正确)。Opera 将同时渲染两者正确。这显然是一个 Firefox 错误,但这只是作为希望使用 lxml 的无前缀 XHTML 的理由——还有其他很好的理由可以减少移动客户端的流量等(h:如果您考虑数十或数百个 html 标签,甚至会相当多) .

4

3 回答 3

8

使用ElementMaker并给它一个nsmap映射None到您的默认命名空间的映射。

#!/usr/bin/env python
# dogeml.py

from lxml.builder import ElementMaker
from lxml import etree

E = ElementMaker(
    nsmap={
        None: "http://wow/"    # <--- This is the special sauce
    }
)

doge = E.doge(
    E.such('markup'),
    E.many('very namespaced', syntax="tricks")
)

options = {
    'pretty_print': True,
    'xml_declaration': True,
    'encoding': 'UTF-8',
}

serialized_bytes = etree.tostring(doge, **options)
print(serialized_bytes.decode(options['encoding']))

正如您在此脚本的输出中看到的那样,定义了默认命名空间,但标签没有前缀。

<?xml version='1.0' encoding='UTF-8'?>
<doge xmlns="http://wow/">
   <such>markup</such>
   <many syntax="tricks">very namespaced</many>
</doge>

我已经使用 Python 2.7.6、3.3.5 和 3.4.0 以及 lxml 3.3.1 测试了这段代码。

于 2014-04-07T01:50:42.193 回答
2

此 XSL 转换从 中删除所有前缀content,同时保留根节点中定义的命名空间:

import lxml.etree as ET

content = '''\
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE html>
<h:html xmlns:h="http://www.w3.org/1999/xhtml" xmlns:ml="http://foo">
  <h:head>
    <h:title>MathJax Test Page</h:title>
    <h:script type="text/javascript"><![CDATA[
      function test() {
        alert(document.getElementsByTagName("p").length);
      };
    ]]></h:script>
  </h:head>
  <h:body onload="test();">
    <h:p>test</h:p>
    <ml:foo></ml:foo>
  </h:body>
</h:html>
'''
dom = ET.fromstring(content)

xslt = '''\
<xsl:stylesheet version="1.0"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="no"/>

<!-- identity transform for everything else -->
<xsl:template match="/|comment()|processing-instruction()|*|@*">
    <xsl:copy>
      <xsl:apply-templates />
    </xsl:copy>
</xsl:template>

<!-- remove NS from XHTML elements -->
<xsl:template match="*[namespace-uri() = 'http://www.w3.org/1999/xhtml']">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@*|node()" />
    </xsl:element>
</xsl:template>

<!-- remove NS from XHTML attributes -->
<xsl:template match="@*[namespace-uri() = 'http://www.w3.org/1999/xhtml']">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="." />
    </xsl:attribute>
</xsl:template>
</xsl:stylesheet>
'''

xslt_doc = ET.fromstring(xslt)
transform = ET.XSLT(xslt_doc)
dom = transform(dom)

print(ET.tostring(dom, pretty_print = True, 
                  encoding = 'utf-8'))

产量

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>MathJax Test Page</title>
    <script type="text/javascript">
      function test() {
        alert(document.getElementsByTagName("p").length);
      };
    </script>
  </head>
  <body onload="test();">
    <p>test</p>
    <ml:foo xmlns:ml="http://foo"/>
  </body>
</html>
于 2012-11-27T19:43:24.363 回答
1

要扩展@neirbowj 的答案,但使用 ET.Element 和 ET.SubElement,并使用混合命名空间呈现文档,其中根恰好是显式命名空间,而子元素 ( channel) 是默认命名空间:

# I set up but don't use the default namespace:
root = ET.Element('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF', nsmap={None: 'http://purl.org/rss/1.0/'})
# I use the default namespace by including its URL in curly braces:
e = ET.SubElement(root, '{http://purl.org/rss/1.0/}channel')
print(ET.tostring(root, xml_declaration=True, encoding='utf8').decode())

这将打印出以下内容:

<?xml version='1.0' encoding='utf8'?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><channel/></rdf:RDF>

它自动使用 rdf 作为 RDF 命名空间。我不确定它是如何计算出来的。如果我想指定它,我可以将它添加到根元素中的 nsmap 中:

nsmap = {None: 'http://purl.org/rss/1.0/',
         'doge': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
root = ET.Element('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF', nsmap=nsmap)
e = ET.SubElement(root, '{http://purl.org/rss/1.0/}channel')
print(ET.tostring(root, xml_declaration=True, encoding='utf8').decode())

...我明白了:

<?xml version='1.0' encoding='utf8'?>
<doge:RDF xmlns:doge="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/"><channel/></doge:RDF>
于 2019-02-16T00:54:38.167 回答