在Python中解析HTML - 使用lxml或BeautifulSoup?这两者在哪些情况下更好?

63

据我所了解,Python中两个主要的HTML解析库是lxml和BeautifulSoup。我选择使用BeautifulSoup来完成正在进行的项目,但这只是因为我发现它的语法比较易学易懂,并没有特别的原因。尽管如此,我发现很多人似乎更喜欢lxml,并且我也听说lxml更快。

那么,一个库相对于另一个库有哪些优点呢?在什么情况下应该使用lxml呢?在什么情况下使用BeautifulSoup会更好?还有其他值得考虑的库吗?


可能是BeautifulSoup和lxml.html - 该选择哪个?的重复问题。我已经写了一个详细的答案; 在这里重新发布,因为问题是重复的。 - Sergey Orshanskiy
抱歉,我本来想关闭另一个的。现在已经标记了另一个。我以为提高旗帜的位置无所谓,可以在旧的或新的里面。 - Sergey Orshanskiy
7个回答

26

Pyquery 提供了类似于jQuery选择器的接口,可在 Python 中使用(内部使用 lxml 库)。

http://pypi.python.org/pypi/pyquery

它真的很棒,我再也不用其他东西了。


我一直想尝试这个库。看起来很有趣。 - Dan Gayle
这比bs4更好用。我在bs4上遇到了一些问题,其中“诊断”甚至都无法工作:( - Tjorriemorrie

22

首先,BeautifulSoup已经停止活跃维护, 作者甚至推荐使用lxml等替代方案。

引用链接页面中的话:

Beautiful Soup 的版本 3.1.0 对真实世界的 HTML 处理效果要差得多, 比起版本 3.0.8 来。最常见的问题是 处理标签不正确、"malformed start tag"和 "bad end tag" 错误。 这个页面解释了发生了什么,问题将如何解决, 以及你现在可以做什么。

此页面最初编写于2009年3月。 从那时起,3.2系列已发布,代替了3.1系列, 并开始开发4.x系列。为了历史记录, 此页面将保持不变。

简而言之

请改用3.2.0版本。


9
我不知道BeautifulSoup会逐渐失效,而我却一直依赖和喜爱它。 - Jonathan Feinberg
1
好的,lxml表示它具有良好的性能,而这里有人说BeautifulSoup的性能非常慢。它似乎也有不错的API。http://codespeak.net/lxml/performance.html - JohnnySoftware
20
个人认为这是误导性的——仔细阅读那个页面会发现 lxml 只是一个替代品,用来取代问题版本 3.1.0,而这些问题在 3.2.0 中已得到解决,并且现在甚至有了发布时间只有两个月的版本 4——因此该模块很难被称为“不再积极维护”。请修改回答 - Eli Bendersky
3
很高兴看到BeautifulSoup再次得到维护。3.2.0版本发布于2010年11月——几乎是这个答案发布后一年。 :) - Alex Brasetvik
2
我怀疑这是否应该成为今天的采纳答案。这里的一切信息都几乎没有什么用处(除了怀旧/历史目的)。 - PascalVKooten
3
当我读到这句话时,我认为bs4已经停用了。很高兴看到有人说:“使用3.2.0版本代替它。” - Wally

20
总之,lxml定位为轻松快捷、生产级别的HTML和XML解析器,顺带还包括一个soupparser模块,以支持BeautifulSoup的功能。 BeautifulSoup是一个由一个人完成的项目,旨在帮助您节省时间,从格式不正确的HTML或XML中快速提取数据。 lxml documentation表示两种解析器都有优点和缺点。因此,lxml提供了一个soupparser,以便您可以来回切换。引用如下,

BeautifulSoup使用不同的解析方法。它不是一种真正的HTML解析器,而是使用正则表达式来深入解析标签汤。因此,在某些情况下它更容易遵循而在其他情况下则可能效果不佳。 lxml/libxml2通常能够更好地解析和修复破损的HTML代码,但BeautifulSoup对于编码检测具有更高的支持率。这很大程度上取决于输入的内容哪种解析器更有效。

最后他们说,
使用这个解析器的缺点是它比lxml的HTML解析器慢很多。所以,如果性能很重要,您可能只想在某些情况下将soupparser作为备选方案。
如果我理解得正确,这意味着soup解析器更加健壮,它可以通过使用正则表达式来处理“垃圾桶”中的格式错误标签,而lxml更直接,只是像预期的那样解析和构建树。我认为它也适用于BeautifulSoup本身,而不仅仅是lxmlsoupparser
他们还展示了如何利用BeautifulSoup的编码检测,同时仍然使用lxml快速解析:
>>> from BeautifulSoup import UnicodeDammit

>>> def decode_html(html_string):
...     converted = UnicodeDammit(html_string, isHTML=True)
...     if not converted.unicode:
...         raise UnicodeDecodeError(
...             "Failed to detect encoding, tried [%s]",
...             ', '.join(converted.triedEncodings))
...     # print converted.originalEncoding
...     return converted.unicode

>>> root = lxml.html.fromstring(decode_html(tag_soup))

(同样的来源:http://lxml.de/elementsoup.html)。

BeautifulSoup的创始人的话说,

That's it! Have fun! I wrote Beautiful Soup to save everybody time. Once you get used to it, you should be able to wrangle data out of poorly-designed websites in just a few minutes. Send me email if you have any comments, run into problems, or want me to know about your project that uses Beautiful Soup.

 --Leonard

引自Beautiful Soup文档

希望现在清楚了。这个工具是一个出色的个人项目,旨在节省你从设计不良的网站中提取数据的时间。目标是立即为您节省时间,完成工作,而不一定是长期节省时间,并且绝对不会优化软件的性能。

此外,来自lxml网站

lxml已经从Python Package Index下载超过两百万次,也直接在许多软件包分发中提供,例如Linux或MacOS-X。

还有,来自为什么选择lxml?

C库libxml2和libxslt具有巨大的优势:...符合标准...功能齐全...快速。 快!快!... lxml是libxml2和libxslt的新Python绑定...


11

不要使用BeautifulSoup,改用lxml.soupparser,这样你就能充分利用lxml的强大之处,并使用BeautifulSoup的优秀功能来处理破损和低质量的HTML。


我知道这个问题很久以前就被发布了,但它仍然有效吗?像2009年那样工作吗? :D - PythonNewbie

5

我曾经非常成功地使用lxml来解析HTML。它似乎能够很好地处理“混乱”的HTML。我强烈推荐它。

这是一个快速测试,用于尝试处理一些丑陋的HTML:

import unittest
from StringIO import StringIO
from lxml import etree

class TestLxmlStuff(unittest.TestCase):
    bad_html = """
        <html>
            <head><title>Test!</title></head>
            <body>
                <h1>Here's a heading
                <p>Here's some text
                <p>And some more text
                <b>Bold!</b></i>
                <table>
                   <tr>row
                   <tr><td>test1
                   <td>test2
                   </tr>
                   <tr>
                   <td colspan=2>spanning two
                </table>
            </body>
        </html>"""

    def test_soup(self):
        """Test lxml's parsing of really bad HTML"""
        parser = etree.HTMLParser()
        tree = etree.parse(StringIO(self.bad_html), parser)
        self.assertEqual(len(tree.xpath('//tr')), 3)
        self.assertEqual(len(tree.xpath('//td')), 3)
        self.assertEqual(len(tree.xpath('//i')), 0)
        #print(etree.tostring(tree.getroot(), pretty_print=False, method="html"))

if __name__ == '__main__':
    unittest.main()

1
当然,我会使用EHP。它比lxml更快、更优雅、更简单易用。
请查看https://github.com/iogf/ehp
<body ><em > foo  <font color="red" ></font></em></body>


from ehp import *

data = '''<html> <body> <em> Hello world. </em> </body> </html>'''

html = Html()
dom = html.feed(data)

for ind in dom.find('em'):
    print ind.text()    

输出:

Hello world. 

0

可以在这里找到一个有点过时的速度比较,它明确推荐使用lxml,因为速度差异似乎很大。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接