BeautifulSoup无法解析网页?

5

我现在正在使用Beautiful Soup来解析网页,听说它非常出名和好用,但似乎它不能正常工作。

这是我所做的:

import urllib2
from bs4 import BeautifulSoup

page = urllib2.urlopen("http://www.cnn.com/2012/10/14/us/skydiver-record-attempt/index.html?hpt=hp_t1")
soup = BeautifulSoup(page)
print soup.prettify()

我认为这很简单。我打开网页并将其传递给beautifulsoup。但是这就是我得到的结果:
警告(来自warnings模块):
文件“C:\ Python27 \ lib \ site-packages \ bs4 \ builder_htmlparser.py”,第149行
“Python内置的HTMLParser无法解析给定的文档。 这不是Beautiful Soup的错误。 最好的解决方案是安装外部解析器(lxml或html5lib),并使用该解析器使用Beautiful Soup。 有关帮助,请参见http://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser。”
...
HTMLParseError:错误的结束标记:u'</"+"script>',位于第634行,第94列。
我认为CNN网站应该设计良好,所以我不太确定发生了什么。 有人有关于此的想法吗?

我在Python 2.7设置中没有安装bs4,但在3.2和3.3中可以无问题运行。 - poke
4个回答

10

来自官方文档:

如果可以的话,我建议您安装并使用lxml以获得更快的速度。 如果您使用的是早于2.7.3版本的Python 2或早于3.2.2版本的Python 3,则必须安装lxml或html5lib-在旧版本中,Python内置的HTML解析器效果不佳。

如果在Python 2.7(如lxml或html5lib)上安装了更强大的解析器,则您的代码将原封不动地运行(在Python 2.7、Python 3.3上):

try:
    from urllib2 import urlopen
except ImportError:
    from urllib.request import urlopen # py3k

from bs4 import BeautifulSoup # $ pip install beautifulsoup4

url = "http://www.cnn.com/2012/10/14/us/skydiver-record-attempt/index.html?hpt=hp_t1"
soup = BeautifulSoup(urlopen(url))
print(soup.prettify())

HTMLParser.py - 更健壮的 SCRIPT 标签解析 的 bug 也许与此有关。


我认为我正在使用Python2.7.2(目前我无法使用那台计算机,所以我不确定)。因此,如果我安装更好的解析器,比如lxml,我就不需要修改我的代码了吗?(我认为try和except部分是针对urllib而不是Beautifulsoup的)。只是希望确保我正确理解了它。谢谢。 - JLTChiu
@JLTChiu:是的,您不需要修改代码。try/except 可以在 Python 2 和 Python 3 上运行相同的脚本(Python 2 上使用 urllib2,Python 3 上使用 urllib.request)。 - jfs

8
您不能使用BeautifulSoup或任何HTML解析器来读取网页。您永远无法保证网页是一个格式良好的文档。让我解释一下在这个给定的情况下发生了什么。
在该页面上有这个内联JavaScript:
var str="<script src='http://widgets.outbrain.com/outbrainWidget.js'; type='text/javascript'></"+"script>";

您可以看到它正在创建一个字符串,该字符串将在页面上放置一个脚本标记。但是,如果您是HTML解析器,这是一个非常棘手的问题。当您阅读令牌时,突然遇到<script>标记。不幸的是,如果您这样做:
<script>
alert('hello');
<script>
alert('goodby');

大多数解析器会认为:“好的,我找到了一个未关闭的脚本标签。哦,我又找到了一个未关闭的脚本标签!他们肯定忘记关闭第一个脚本标签了!”,解析器就会认为这两个标签都是有效的脚本。
因此,在这种情况下,BeautifulSoup看到了一个<script>标签,尽管它在JavaScript字符串内部,但它看起来像是一个有效的起始标签,因此BeautifulSoup就会出现异常,这也是应该的。
如果你再看一遍这个字符串,你会发现这个有趣的工作细节:
... "</" + "script>";

这看起来很奇怪,是吗?不做额外的字符串拼接直接使用str = " ... </script>"不是更好吗?实际上,这是一个常见的技巧(由愚蠢的人编写脚本标记作为字符串,这是一种不良的做法),以使解析器不会中断。因为如果你这样做:
var a = '</script>';

在内联脚本中,解析器会看到</script>标签,然后认为整个脚本标签已经结束,并将该标签的其余内容作为纯文本呈现在页面上。这是因为你可以在任何地方放置一个闭合脚本标签,即使你的JS语法无效。从解析器的角度来看,尽早退出脚本标签比尝试将HTML代码呈现为JavaScript更好。
所以,你不能使用常规的HTML解析器来解析网页,这是非常危险的游戏。不能保证你得到的HTML是格式正确的。根据你想要做什么,你可以使用正则表达式读取页面内容,或者尝试使用无界面浏览器获取完全渲染的页面内容。

2
“你不能使用任何HTML解析器来读取网页” - 我认为这是一个错误的说法。网络浏览器正是这样做的,它们使用一个成熟的HTML解析器来解析网页的内容。当然,它们还添加了许多其他功能,如评估脚本等,但它们仍然首先解析基本的HTML。在这种情况下,内置的解析器似乎无法接受特定的HTML(尽管它对我和Vor也很好用),因此需要更强大的解析器。但它仍然是一个HTML解析器。 - poke

2

您需要使用BeautifulSoup结合html5lib解析器。

安装所需的解析器,请使用pip:

pip install html5lib

然后这样使用解析器。
import mechanize
br = mechanize.Browser()
html = br.open("http://google.com/",timeout=100).read()
soup = BeautifulSoup(html,'html5lib')
a_s = soup.find_all('a')
for i in range(0,len(a_s)):
 print a_s[i]['href']

1

你可以做的最简单的事情之一是,将内容指定为“lxml”。您可以通过在urlopen()函数中添加“lxml”作为参数来实现。

page = urllib2.urlopen("[url]","lxml")

然后您的代码将如下所示。

import urllib2from bs4 import BeautifulSoup page = urllib2.urlopen("http://www.cnn.com/2012/10/14/us/skydiver-record-attempt/index.html?hpt=hp_t1","lxml") soup = BeautifulSoup(page) print soup.prettify()

到目前为止,我没有从这种方法中遇到任何问题:)


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