2012-11-27 103 views
2

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

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

是否有任何其他方式删除XHTML命名空间的命名空间前缀?请注意,str.replace,正如对另一个相关问题的答案中所建议的,是而不是我可以接受的一种方法,因为它不知道XML语义,并且可能容易搞砸所产生的文档。

根据要求,你会发现两个例子可以使用。一个与namespace prefixesone without。第一个将在Firefox中显示0(错误),第二个将显示1(正确)。 Opera将渲染两个正确的。这显然是a Firefox bug,但这仅仅是希望用lxml获得前缀XHTML的理由 - 还有其他一些很好的理由来减少移动客户端的流量等等(如果你考虑数十或者更多的html标签,甚至可以使用h:)。

+0

样本XHTML是否可用?一个可以用于其他人测试/使用的小型自包含示例? –

+0

我会在一分钟内设置一个示例。 –

回答

2

This XSL transformation删除所有前缀从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> 
+0

我不想删除所有的命名空间,我想使它无前缀。 ''xmlns''仍然需要存在。 –

+0

这样比较好,但XHTML仍然无效。有没有办法将''xmlns:h''属性修改为''xmlns''? –

+0

没关系,我通过在''xsl:stylesheet />''中设置''xmlns =“http:// .../xhtml''来实现它的功能。谢谢您的输入,我将编辑并接受:) –

3

使用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结合测试了这段代码。

+1

谢谢!这对创建文档很有帮助,但是我的输入大部分是来自XSL转换或加载的文件,但这可能与其他人有关,因此+1 –

+0

确定。提到“渲染XHTML” – neirbowj

+0

+1 Dogememe参考 –

相关问题