司开星的博客

lxml.html 中几种解析器的区别

lxml 自2.0版本起有了专门用于处理html的模块lxml.html, 模块中包含几种解析html的方式, 以载入html文档为例:

1
2
3
4
5
6
7
from lxml.html import fromstring, soupparser, html5lib

html = '<html><a>x=1</a></html>'

a = fromstring(html)
b = soupparser.fromstring(html)
c = html5lib.fromstring(html)

区别

主要区别

首先看第一种方式, 直接用lxml.html中的fromstring方法载入. 官方文档中原话是It is based on lxml's HTML parser, 从源码中可以发现此方法使用的parser为 etree.HTMLParser的子类, 暂且就称其为lxml解析器.

第二种方式用到了soupparser模块, 官方对此的说明是:

lxml can make use of BeautifulSoup as a parser backend, just like BeautifulSoup can employ lxml as a parser. When using BeautifulSoup from lxml, however, the default is to use Python’s integrated HTML parser in the html.parser module.

也就是这个模块底层是用beautifulsoup来处理html, 指定的parser为Python自带的html.parser.

对于soupparser的优势官方有如下一段话:

A very nice feature of BeautifulSoup is its excellent support for encoding detection which can provide better results for real-world HTML pages that do not (correctly) declare their encoding.

也就是BeautifulSoup对于声明编码与实际编码不一致的情况处理的更好.

第三种方式用到了html5lib模块, 官方说明:

html5lib is a Python package that implements the HTML5 parsing algorithm which is heavily influenced by current browsers and based on the WHATWG HTML5 specification.

大意是html5lib模块实现了与浏览器类似的对HTML5网页的解析算法.

光看几种解释还是有点抽象, 有一张图比较了三种解析器的区别:

解析器 使用方法 优势 劣势
Python标准库 BeautifulSoup(markup, "html.parser") Python的内置标准库执行速度适中文档容错能力强 Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, "lxml") 速度快文档容错能力强 需要安装C语言库
html5lib BeautifulSoup(markup, "html5lib") 最好的容错性以浏览器的方式解析文档生成HTML5格式的文档 速度慢不依赖外部扩展

这张图来自于BeautifulSoup4的官方中文文档, 且文档中推荐使用lxml解释器.

对不规范网页的处理区别

除了上面提到的区别, 这三种解析器对不规范网页(broken html) 的处理方式也不同.

直接放BeautifulSoup4文档中的说明及示例:

HTML解析器之间也有区别,如果被解析的HTML文档是标准格式,那么解析器之间没有任何差别,只是解析速度不同,结果都会返回正确的文档树.

但是如果被解析文档不是标准格式,那么不同的解析器返回结果可能不同.下面例子中,使用lxml解析错误格式的文档,结果

标签被直接忽略掉了:

1
2
3
> BeautifulSoup("<a></p>", "lxml")
> # <html><body><a></a></body></html>
>

使用html5lib库解析相同文档会得到不同的结果:

1
2
3
> BeautifulSoup("<a></p>", "html5lib")
> # <html><head></head><body><a><p></p></a></body></html>
>

html5lib库没有忽略掉

标签,而是自动补全了标签,还给文档树添加了标签.

使用pyhton内置库解析结果如下:

1
2
3
> BeautifulSoup("<a></p>", "html.parser")
> # <a></a>
>

与lxml [7] 库类似的,Python内置库忽略掉了

标签,与html5lib库不同的是标准库没有尝试创建符合标准的文档格式或将文档片段包含在标签内,与lxml不同的是标准库甚至连标签都没有尝试去添加.

因为文档片段“

”是错误格式,所以以上解析方式都能算作”正确”,html5lib库使用的是HTML5的部分标准,所以最接近”正确”.不过所有解析器的结构都能够被认为是”正常”的.

虽然写的是BeautifulSoup4中的解析器, 但例子中的三种解析器正好与lxml.html中的三种解析器对应.

同时lxml官方文档中也有对此的一些说明:

There are a few differences in the returned tree to the regular HTML parsing functions from lxml.html. html5lib normalizes some elements and element structures to a common format. For example even if a tables does not have a tbody html5lib will inject one automatically:

1
2
3
4
>>>> from lxml.html import tostring, html5parser
>>>> tostring(html5parser.fromstring("<table><td>foo"))
>'<table><tbody><tr><td>foo</td></tr></tbody></table>'
>

The normal HTML parser is capable of handling broken HTML, but for pages that are far enough from HTML to call them ‘tag soup’, it may still fail to parse the page in a useful way. A way to deal with this is ElementSoup, which deploys the well-known BeautifulSoup parser to build an lxml HTML tree.