为啥 BeautifulSoup 无法正确读取/解析此 RSS (XML) 文档?

Posted

技术标签:

【中文标题】为啥 BeautifulSoup 无法正确读取/解析此 RSS (XML) 文档?【英文标题】:Why is BeautifulSoup unable to correctly read/parse this RSS (XML) document?为什么 BeautifulSoup 无法正确读取/解析此 RSS (XML) 文档? 【发布时间】:2012-12-07 08:42:26 【问题描述】:

YCombinator 很好地提供了一个RSS feed 和一个big RSS feed,其中包含HackerNews 上的***项目。我正在尝试编写一个 python 脚本来访问 RSS 提要文档,然后使用 BeautifulSoup 解析出某些信息。但是,当 BeautifulSoup 尝试获取每个项目的内容时,我遇到了一些奇怪的行为。

以下是 RSS 提要的几行示例:

<rss version="2.0">
<channel>
<title>Hacker News</title><link>http://news.ycombinator.com/</link><description>Links for the intellectually curious, ranked by readers.</description>
<item>
    <title>EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and &#39;Notch&#39;</title>
    <link>https://www.eff.org/press/releases/eff-patent-project-gets-half-million-dollar-boost-mark-cuban-and-notch</link>
    <comments>http://news.ycombinator.com/item?id=4944322</comments>
    <description><![CDATA[<a href="http://news.ycombinator.com/item?id=4944322">Comments</a>]]></description>
</item>
<item>
    <title>Two Billion Pixel Photo of Mount Everest (can you find the climbers?)</title>
    <link>https://s3.amazonaws.com/Gigapans/EBC_Pumori_050112_8bit_FLAT/EBC_Pumori_050112_8bit_FLAT.html</link>
    <comments>http://news.ycombinator.com/item?id=4943361</comments>
    <description><![CDATA[<a href="http://news.ycombinator.com/item?id=4943361">Comments</a>]]></description>
</item>
...
</channel>
</rss>

这是我编写的代码(在 python 中),用于访问此提要并打印出每个项目的 titlelinkcomments

import sys
import requests
from bs4 import BeautifulSoup

request = requests.get('http://news.ycombinator.com/rss')
soup = BeautifulSoup(request.text)
items = soup.find_all('item')
for item in items:
    title = item.find('title').text
    link = item.find('link').text
    comments = item.find('comments').text
    print title + ' - ' + link + ' - ' + comments

但是,此脚本给出的输出如下所示:

EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and &#39;Notch&#39; -  - http://news.ycombinator.com/item?id=4944322
Two Billion Pixel Photo of Mount Everest (can you find the climbers?) -  - http://news.ycombinator.com/item?id=4943361
...

如您所见,中间的项目link 不知何故被省略了。也就是说,link 的结果值在某种程度上是一个空字符串。那为什么会这样呢?

当我深入研究 soup 中的内容时,我意识到它在解析 XML 时会以某种方式窒息。这可以通过查看items 中的第一项来看出:

>>> print items[0]
<item><title>EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and &#39;Notch&#39;</title></link>https://www.eff.org/press/releases/eff-patent-project-gets-half-million-dollar-boost-mark-cuban-and-notch<comments>http://news.ycombinator.com/item?id=4944322</comments><description>...</description></item>

您会注意到仅使用link 标记就发生了一些奇怪的事情。它只是获取关闭标签,然后是该标签的文本。这是一些非常奇怪的行为,尤其是与 titlecomments 被毫无问题地解析相比。

这似乎是 BeautifulSoup 的问题,因为请求实际读取的内容没有任何问题。我不认为它仅限于 BeautifulSoup,因为我也尝试使用 xml.etree.ElementTree API 并且出现了同样的问题(BeautifulSoup 是基于此 API 构建的吗?)。

有谁知道为什么会发生这种情况,或者我如何仍然可以使用 BeautifulSoup 而不会出现此错误?

注意:我终于能够使用 xml.dom.minidom 获得我想要的东西,但这似乎不是一个强烈推荐的库。如果可能,我想继续使用 BeautifulSoup。

更新:我在 Mac 上运行 OSX 10.8,使用 Python 2.7.2 和 BS4 4.1.3。

更新 2:我有 lxml,它是用 pip 安装的。它是 3.0.2 版。至于 libxml,我检查了 /usr/lib,显示的是 libxml2.2.dylib。不确定何时或如何安装。

【问题讨论】:

你确定这是真正的 RSS 吗?因为我刚刚用ElementTreecElementTreelxml 实现对其进行了测试,它们都得到了link 节点就好了。所以,要么你对xml.etree.ElementTree 做错了,要么你没有给我们正确的输入数据。 另外,您使用的是什么版本的 Python 和 BS4? (你是否安装了 lxml,如果有,libxml 和 lxml 的版本是什么?)我刚刚完成了 Python 3.3.0 和 BS4 4.1.3 的全新安装,它能够很好地解析该 RSS。使用 Apple Python 2.7.2 和 BS4 4.1.3,同样没问题。 @abarnert 我已经使用请求和 urllib2 直接从 news.ycombinator.com/rss 拉取。因此,除非 ycombinator 提供的内容有问题,否则我不知道 RSS 可能存在什么问题。 那么,您是否通过直接从您的代码看到的相同内容中复制和粘贴来获得上面的 RSS?还是有可能您的代码在一段时间内遇到了损坏的 RSS 提要,但当您将其粘贴到此处时,它已修复? (事实上​​,jdotjdot 在您粘贴的代码中看到了同样的问题,这意味着这实际上是您的两个版本的 BS4 有问题,而不是那个 RSS,但在我问的时候我不知道。) 另外,你在什么平台上? 【参考方案1】:

哇,好问题。这让我觉得是 BeautifulSoup 中的一个错误。您无法使用 soup.find_all('item').link 访问链接的原因是,当您第一次将 html 加载到 BeautifulSoup 中时,它对 HTML 做了一些奇怪的事情:

>>> from bs4 import BeautifulSoup as BS
>>> BS(html)
<html><body><rss version="2.0">
<channel>
<title>Hacker News</title><link/>http://news.ycombinator.com/<description>Links
for the intellectually curious, ranked by readers.</description>
<item>
<title>EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and 'No
tch'</title>
<link/>https://www.eff.org/press/releases/eff-patent-project-gets-half-million-d
ollar-boost-mark-cuban-and-notch
    <comments>http://news.ycombinator.com/item?id=4944322</comments>
<description>Comments]]&gt;</description>
</item>
<item>
<title>Two Billion Pixel Photo of Mount Everest (can you find the climbers?)</ti
tle>
<link/>https://s3.amazonaws.com/Gigapans/EBC_Pumori_050112_8bit_FLAT/EBC_Pumori_
050112_8bit_FLAT.html
    <comments>http://news.ycombinator.com/item?id=4943361</comments>
<description>Comments]]&gt;</description>
</item>
...
</channel>
</rss></body></html>

仔细看——它实际上已经将第一个&lt;link&gt; 标记更改为&lt;link/&gt;,然后删除了&lt;/link&gt; 标记。我不确定它为什么会这样做,但如果不解决 BeautifulSoup.BeautifulSoup 类初始化中的问题,您现在将无法使用它。

更新:

我认为你现在最好的(尽管是 hack-y)赌注是使用以下link

>>> soup.find('item').link.next_sibling
u'http://news.ycombinator.com/'

【讨论】:

是的,我注意到它弄乱了link 标签,在缩小范围后,这似乎与 BeautifulSoup(或 BS 的依赖项)有关。关于幕后发生的事情或如何解决此问题的任何想法? @Treebranch 还不知道发生了什么或如何修复,但给了你一个创可贴 @jdotjdot:您使用的是哪个版本的 BS4?正如我在问题的 cmets 中提到的,在干净的 Python.org 3.3.0 和 Apple 预装的 2.7.2 上全新安装 BS4 4.1.3,我看不到这个错误。 @abarnert 我正在使用 BS4 4.1.3 我问Treebranch,你有没有安装lxml?如果是这样,哪些版本的 libxml 和 lxml(以及它们是如何安装的)?因为 IIRC,如果 lxml 可用,BS 会尝试使用它。哦,什么平台。【参考方案2】:

实际上,问题似乎与您使用的解析器有关。默认情况下,使用 HTML 格式。安装 lxml 模块后尝试使用 soup = BeautifulSoup(request.text, 'xml')。

然后它将使用 XML 解析器而不是 HTML 解析器,应该一切正常。

更多信息请见http://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser

【讨论】:

感觉是 lxml 解析器在做这个。【参考方案3】:

@Yan Hudon 是对的。我用soup = BeautifulSoup(request.text, 'xml') 解决了这个问题

【讨论】:

【参考方案4】:

我认为这里的 BeautifulSoup 没有错误。

我从 OS X 10.8.2 在 Apple 的股票 2.7.2 上安装了 BS4 4.1.3 的干净副本,一切正常。它不会将&lt;link&gt; 误解析为&lt;/link&gt;,因此它不存在item.find('link') 的问题。

我还尝试在 2.7.2 中使用股票 xml.etree.ElementTreexml.etree.cElementTree,在 python.org 3.3.0 中使用 xml.etree.ElementTree 来解析相同的东西,它再次运行良好。代码如下:

import xml.etree.ElementTree as ET

rss = ET.fromstring(x)
for channel in rss.findall('channel'):
  for item in channel.findall('item'):
    title = item.find('title').text
    link = item.find('link').text
    comments = item.find('comments').text
    print(title)
    print(link)
    print(comments)

然后我使用 Apple 的内置 /usr/lib/libxml2.2.dylib(根据xml2-config --version,是 2.7.8)安装了 lxml 3.0.2(我相信 BS 使用 lxml,如果有的话),并使用它的etree,并使用 BS,再次,一切正常。

除了搞砸了&lt;link&gt;,jdotjdot 的输出还显示BS4 以一种奇怪的方式搞砸了&lt;description&gt;。原文是这样的:

<description><![CDATA[<a href="http://news.ycombinator.com/item?id=4944322">Comments</a>]]></description>

他的输出是:

<description>Comments]]&gt;</description>

我运行他完全相同的代码的输出是:

<description><![CDATA[<a href="http://news.ycombinator.com/item?id=4944322">Comments</a>]]></description>

所以,这里似乎存在更大的问题。奇怪的是,它发生在两个不同的人身上,而它并没有发生在全新安装任何东西的最新版本时。

这意味着要么这是一个已修复的错误,而我只是有一个更新版本的有错误的东西,要么是他们俩安装某些东西的方式有些奇怪。

BS4本身可以排除,因为至少Treebranch和我一样有4.1.3。虽然,在不知道他是如何安装的情况下,这可能是安装的问题。

可以排除 Python 及其内置的 etree,因为至少 Treebranch 拥有与我相同的 OS X 10.8 的 Apple 2.7.2。

这很可能是 lxml 或底层 libxml 或它们的安装方式的错误。我知道 jdotjdot 有 lxml 2.3.6,所以这可能是一个在 2.3.6 和 3.0.2 之间修复的错误。事实上,鉴于lxml website 和 2.3.5 之后的任何版本的更改说明,没有没有 2.3.6,所以无论他有什么可能是某种错误的发布很早就取消了一个分支什么的……我不知道他的 libxml 版本,或者是如何安装的,或者他在什么平台上,所以很难猜测,但至少这是可以调查的。

【讨论】:

以上是关于为啥 BeautifulSoup 无法正确读取/解析此 RSS (XML) 文档?的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Beautifulsoup 从网站读取表格

无法从 beautifulsoup 中正确打印出组合表

我有这个错误:无法读取 null 的属性“值”,我不明白为啥

有没有办法通过熊猫读取BeautifulSoup输出以读取表?

python爬虫时,bs4无法读取网页标签中的文本?

有没有办法使用 BeautifulSoup 将数据从列表正确转换为 CSV 文件?