如何使用标准库在Python中解析格式错误的HTML

38

Python内置了许多用于处理HTML和XML的,很难相信没有支持现实世界HTML解析的功能。

我找到了很多非常棒的第三方库来完成这个任务,但是这个问题是关于Python标准库的。

要求:

  • 只使用Python标准库组件(任何2.x版本)
  • 支持DOM
  • 处理HTML实体(如 
  • 处理部分文档(例如:Hello, <i>World</i>!

加分项:

  • XPATH支持
  • 处理未闭合/格式不正确的标签。(例如:<big>does anyone here know <html ???

这是我所提供的90%解决方案。它对我尝试过的有限HTML集合有效,但众所周知,它并不是非常健壮。由于我只是在看了15分钟的文档和一行代码后就完成了这个问题,所以我认为我可以向stackoverflow社区咨询类似但更好的解决方案...

from xml.etree.ElementTree import fromstring
DOM = fromstring("<html>%s</html>" % html.replace('&nbsp;', '&#160;'))

2
我不明白。你希望我们做什么?你知道stdlib中没有这样的模块。你的问题是什么? - SilentGhost
2
如果您可以使用标准库完成90%的工作,请指出一些明确的例子,说明您无法完成的部分。如果您在工作中轻松传递Python脚本,那么您的受众群体不应太担心安装一个漂亮的打包库需要花费15秒钟,尤其是如果您已经将其下载到内部网络并在电子邮件中提供了方便的链接。如果您是系统管理员,也许可以重新打包一堆有用的库并将它们推送出去? - Nick T
2
@SilentGhost:Python 的一个常见座右铭是“电池包含在内”,这意味着您应该能够使用 stdlib 完成大多数任务。也许 HTML DOM 不是其中之一。这就是这个问题试图澄清的内容。 - bukzor
3
正如@mikerobi所指出的那样,BeautifulSoup源代码非常小,因此如果你真的想要一个没有第三方依赖的单文件脚本,复制粘贴似乎是最好的选择,可以跳过尝试拼凑一些标准库的步骤。 - Nick T
1
一个老问题,但必须要说的是:最后10%仍然占据了90%(或更多)的工作量。 - mc0e
显示剩余7条评论
6个回答

47

可靠地解析HTML是相对现代的发展(尽管这似乎很奇怪)。因此,标准库中肯定没有任何东西。 HTMLParser 可能看起来是处理HTML的一种方法,但它实际上并不行--它无法处理许多非常常见的HTML,虽然您可以绕过这些失败,但总会有另一种情况您没有考虑到(如果您确实成功处理每个失败,那么你基本上已经重新创建了BeautifulSoup)。

解析网络上的HTML只有3种合理的方法:lxml.htmlBeautifulSouphtml5lib。lxml是迄今为止最快的,但安装可能有些棘手(在App Engine等环境中可能无法实现)。html5lib是基于HTML 5规范解析的,虽然在实践中与其他两者类似,但在解析损坏的HTML方面可能更加“正确”(它们都以相同的方式解析相当不错的HTML)。它们都能够很好地解析损坏的HTML。尽管我发现BeautifulSoup的API是不必要地奇怪,但它可能很方便。


好答案。谢谢!我没有足够的声望给你点赞。QQ我希望人们对于难题不要那么敏感。好的科学家也会寻求负实验。 - bukzor
@Ian Bicking:终于有足够的声望来提升你了。只是确认一下,没有已知的方法可以让ElementTree(在stdlib中存在)解析现实世界的HTML吗? - bukzor
你可以使用BeautifulSoup(带有ElementSoup)或html5lib解析HTML并生成ElementTree结构,但是ElementTree本身绝对无法解析HTML。 - Ian Bicking
1
通过一些调整和少量的HTML修正,我已经让ElementTree解析了RosettaCode.org的所有内容。最烦人的部分是手动将所有HTML实体添加到解析器中。即使在etree文档中有一个选项可以实现这一点,但由于未记录的原因而未实现。您可以在此处查看代码:http://bukzor.hopto.org/svn/software/python/rosetta_pylint.py - bukzor

5

将BeautifulSoup的源代码复制到您的脚本中即可;-) 我只是开玩笑...您可以编写的任何内容都会或多或少地重复已经存在于类似库中的功能。

如果这真的行不通,我必须问一下,为什么只使用标准库组件如此重要呢?


这不是很重要,只是我的问题。正如我所说,Python库中有大量的HTML和XML支持。似乎应该有一些东西支持这个。如果没有,那也是一个答案,但我还没有被说服。 - bukzor
请注意,BeautifulSoup已不再维护。我个人更喜欢使用lxml.html。总的来说,这是一个很好的答案。 - Mike Graham
你从哪里听说的?BeautifulSoup网站没有显示它已经不再维护的证据。事实上,最近的发布是11天前。(当然,任何其他第三方HTML解析器都可以很好地完成我在答案中提出的论点) - David Z
也许他认为 BS 3.0 只适用于 Python 3.x?他们的网站表明,BS 3.0 适用于 Py 2.3-2.6,而 BS 3.1 适用于 Py 3.x(尽管具有讽刺意味的是,BS 3.1 的最后一个版本已经有一年之久了,而 BS 3.0 则只有几周时间)。 - Nick T
1
@bukzor,ElementSoup是使用BeautifulSoup进行解析的ElementTree实现。 ElementTree是一个具有许多实现的API,用于解析XML和HTML。 - Mike Graham
显示剩余7条评论

4
你的选择是更改你的需求或者复制第三方模块开发人员完成的所有工作。
Beautiful Soup由一个包含约2000行代码的Python文件组成,如果这个依赖过于庞大,那么你可以自己编写,但它不会像Beautiful Soup一样工作得好,并且可能不会小很多。

1
如果它真的那么紧凑(从来没有真正去看过:P),而且他非常坚决地希望脚本在没有任何其他依赖项的情况下工作,复制粘贴听起来是一个很好的计划。 - Nick T
5
字面复制粘贴是一种荒谬的添加依赖的方式。 - Mike Graham

1

这是我在这个任务中引用的库之一:“我已经找到了很多非常好的第三方库来完成这个任务,但这个问题是关于Python标准库的。” - bukzor

1

我想不出任何使用流行语言的标准库拥有良好、强大、启发式的HTML解析库。Python肯定没有,我认为你已经知道这一点。

为什么需要stdlib模块呢?当我听到人们提出这种要求时,大多数情况下他们都在开玩笑。对于大多数主要任务,您将需要第三方模块或花费很多工作来重新实现一个模块。引入依赖关系是一件好事,因为这是您不必做的工作。

所以你想要的是lxml.html。如果有问题,请将lxml与您的代码一起发布,此时它在功能上等同于自己编写,除了困难程度、错误和可维护性之外。


1
从我的研究来看,这似乎是最常见的答案,但我并不确定,而且我仍然不相信标准库中没有这样的功能。你必须承认,一个不使用外部库的脚本对于初学者来说更有可能正常工作。 - bukzor
@bukzor,既然是这样,我们就信服了。=p 我根本不需要承认。;) - Mike Graham
5
解析HTML这件事,人们只有在最近几年才能广泛理解;这花费了惊人的长时间。因此可以明确地说,在标准库中没有任何东西:BeautifulSoup、html5lib和lxml.html构成了一个完整的清单。 - Ian Bicking
1
@Ian Bicking:如果你把那个变成回答,我就会点赞。难道我的回答因为是否定的原因而被降分了吗? - bukzor

0

正如已经提到的,目前没有标准库可以提供令人满意的解决方案。当我尝试在一个过时的托管环境上运行我的程序时,我遇到了与您相同的问题,而且只能使用python2.6,无法安装自己的扩展。解决方案:

下载此文件和最新稳定版的3er系列BeautifulSoup(目前为3.2.1)。从那里的tar文件中,只选择BeautifulSoup.py,这是你真正需要随代码一起发布的唯一文件。所以,当你的路径中有这两个文件时,你所需要做的就是像从lxml中获取一样,从一些HTML字符串中获取一个休闲的etree对象:

from StringIO import StringIO
import ElementSoup

tree = ElementSoup.parse(StringIO(input_str))

lxml和html5lib都需要您编译一些C代码才能运行。要让它们正常工作需要投入相当大的精力,如果您的环境受到限制或您的目标受众不愿意这样做,请避免使用它们。


2
html5lib没有依赖于任何扩展(例如C代码)。它可以选择性地使用几个(如datrie)来提高性能,但即使没有这些扩展也可以正常工作。 - gsnedders

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