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 html5解析器来尊重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命名空间。 在html5lib源代码中插入一些printf,我观察到:
- lxml 实际上是调用html5lib与
namespaceHTMLElements=False
如预期的 - 但是, lxml似乎调用到html5lib 两次:第一次没有
namespaceHTMLElements
,然后第二次与namespaceHTMLElements=False
- lxml 实际上是调用html5lib与
查找原因的结论
鉴于上述情况,很明显问题出在lxml和html5lib之间的接口上。我不知道为什么lxml调用到html5lib两次,但我认为这可能是因为出于某种原因,它首先尝试创建自己的XHTMLParser
的新实例之前做什么我实际上要求它做,这只是创建自己的HTMLParser
的实例。
因此,它确实对html5lib进行了两次调用,这可能导致html5lib在某种程度上"锁定"了第一次调用产生的默认namespaceHTMLElements=True
行为,然后在第二次调用中看到namespaceHTMLElements=False
指令时忽略它。
也许在进行两次调用的方式,lxml是打破了一些假设在html5lib,或实际上是误用html5lib API的方式,它的设计是不打算被使用。
或者原因根本不是lxml对html5lib进行两次单独调用的结果,而是它使用html5lib接口的方式出现了其他问题。
无论如何,我很想听听其他人是否遇到过这个问题并有解决办法,或者至少对为什么会发生这种情况有一些见解。
我在源代码中遵循了lxml如何将参数传递给html5lib。大多数函数都有一个结束*kws,然后将其传递给下一个函数。在调用实际的html5解析器的最后一个步骤中,这将被删除,解析器将使用2个固定参数调用。
(我昨天遇到了同样的问题,刚刚遇到这个问题,忘记了细节,请允许我放弃任何代码片段和引用。)
无论如何,这证实了在2018年,如果由于某种原因不能调用lxml自己的解析器,直接调用html5lib仍然是首选的方式。
(我的用例是:解析蹩脚的html并使用xpath)