lxml html5parser似乎忽略namespaceHTMLElements=False
了我传递给它的任何选项。它将我给它的所有元素放入 HTML 命名空间,而不是(预期的)void 命名空间。
这是一个重现问题的简单案例:
echo "<p>" | python -c "from sys import stdin; \
from lxml.html import html5parser as h5, tostring; \
print tostring(h5.parse(stdin, h5.HTMLParser(namespaceHTMLElements=False)))"
输出是这样的:
<html:html xmlns:html="http://www.w3.org/1999/xhtml"><html:head></html:head><html:body><html:p>
</html:p></html:body></html:html>
可以看出,html
元素和所有其他元素都在 HTML 命名空间中。
预期的输出是这样的:
<html><head></head><body><p>
</p></body></html>
我认识到这namespaceHTMLElements
是一个 html5lib 选项,而不是 lxml 直接执行任何操作的本机 lxml 选项。lxml 应该只调用 html5lib 并将该选项传递给 html5lib,以便 html5lib 按预期使用它。
2016-02-17 更新
我仍然没有找到让 lxml html5parser 尊重namespaceHTMLElements
. 但需要明确的是,替代方法是直接调用 html5lib,如下所示:
echo "<p>" | python -c "from sys import stdin; \
import html5lib; from lxml import html; \
doc = html5lib.parse(stdin, treebuilder='lxml', namespaceHTMLElements=False); \
print html.tostring(doc)"
更多细节
我已经知道的一些事情:
- html5lib 完全符合 HTML 规范的要求,包括
html
元素必须放在 HTML 命名空间中的要求——html5lib 就是这样做的 - 但是,html5lib 提供
namespaceHTMLElements=False
了一个选项来覆盖默认的“将html
元素放入 HTML 命名空间”行为。 - 当我直接使用 html5lib(而不是通过 lxml)并将其传递
namespaceHTMLElements=False
给它时,一切都按预期工作 -html
元素进入 void 命名空间。 将一些 printf 破解到 html5lib 源代码中,我观察到:
- lxml实际上
namespaceHTMLElements=False
是按预期调用 html5lib - 但是,lxml 似乎两次调用 html5lib :第一次没有
namespaceHTMLElements
,然后第二次使用namespaceHTMLElements=False
- lxml实际上
关于在哪里找到原因的结论
综上所述,很明显问题出在 lxml 和 html5lib 之间的接口上。我不确定为什么 lxml 两次调用 html5lib 但我认为这可能是因为出于某种原因它首先尝试创建自己的新实例,XHTMLParser
然后再执行我实际要求它执行的操作,这只是创建它自己的一个实例HTMLParser
。
因此,它确实对 html5lib 进行了两次调用这一事实可能会导致 html5lib 对namespaceHTMLElements=True
第一次调用产生的默认行为进行排序,然后namespaceHTMLElements=False
在第二次调用中看到该指令时忽略该指令。
也许在以它的方式进行两次调用时,lxml 要么打破了 html5lib 中的某些假设,要么实际上以一种设计上不打算使用的方式滥用 html5lib API。
或者,原因可能根本不是 lxml 对 html5lib 进行两次单独调用的结果,而是它使用 html5lib 接口的方式存在其他问题。
无论如何,我很想听听其他人是否有其他人遇到过这个问题并有解决方法——或者至少对它发生的原因有一些了解。