将Web数据传递到Beautiful Soup - 空列表

时间:2014-07-31 19:40:38

标签: python web-scraping beautifulsoup urllib3 web-content

我已经重新检查了我的代码并查看了打开URL以将Web数据传递到Beautiful Soup的类似操作,由于某些原因我的代码只是没有返回任何内容,尽管它的格式正确:

>>> from bs4 import BeautifulSoup

>>> from urllib3 import poolmanager

>>> connectBuilder = poolmanager.PoolManager()

>>> content = connectBuilder.urlopen('GET', 'http://www.crummy.com/software/BeautifulSoup/')

>>> content
<urllib3.response.HTTPResponse object at 0x00000000032EC390>

>>> soup = BeautifulSoup(content)

>>> soup.title
>>> soup.title.name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'name'
>>> soup.p
>>> soup.get_text()
''

>>> content.data
a stream of data follows...

如图所示,很明显urlopen()返回一个由变量内容捕获的HTTP响应,它有意义的是它可以读取响应的状态,但是在它被传递到Beautiful Soup之后,Web数据就没有了。转换成美丽的汤对象(可变汤)。你可以看到我试图读取一些标签和文本,get_text()返回一个空列表,这很奇怪。

奇怪的是,当我通过content.data访问网页数据时,数据会显示出来,但由于我不能使用Beautiful Soup来解析它,所以它没用。我的问题是什么?感谢。

4 个答案:

答案 0 :(得分:11)

如果您只想抓取该页面,requests将获得您需要的内容:

from bs4 import BeautifulSoup

import requests
r = requests.get('http://www.crummy.com/software/BeautifulSoup/')
soup = BeautifulSoup(r.content)

In [59]: soup.title
Out[59]: <title>Beautiful Soup: We called him Tortoise because he taught us.</title>

In [60]: soup.title.name
Out[60]: 'title'

答案 1 :(得分:11)

urllib3返回一个Response对象,该对象包含具有预加载的主体有效负载的.data

根据最新的快速入门usage example here,我会做这样的事情:

import urllib3
http = urllib3.PoolManager()
response = http.request('GET', 'http://www.crummy.com/software/BeautifulSoup/')

from bs4 import BeautifulSoup
soup = BeautifulSoup(response.data)  # Note the use of the .data property
...

其余的应该按预期工作。

-

关于原始代码中出现的问题:

您传递了整个response对象而不是身体有效负载。这应该是正常的,因为response对象是一个类似文件的对象,除了在这种情况下,urllib3已经消耗了所有响应并为你解析它,所以没有剩下任何东西到.read()。这就像传递已经读过的文件指针一样。另一方面,.data将访问已读取的数据。

如果要将urllib3响应对象用作类文件对象,则需要禁用内容预加载,如下所示:

response = http.request('GET', 'http://www.crummy.com/software/BeautifulSoup/', preload_content=False)
soup = BeautifulSoup(response)  # We can pass the original `response` object now.

现在它应该按预期工作。

我明白这不是很明显的行为,而且作为urllib3的作者我道歉。 :)我们计划有一天将preload_content=False设为默认值。也许有一天很快(I opened an issue here)。

-

关于.urlopen vs .request的快速说明:

.urlopen假设您将处理传递给请求的任何参数的编码。在这种情况下,可以使用.urlopen,因为您没有将任何参数传递给请求,但通常.request会为您完成所有额外的工作,因此更方便。

如果有人愿意改进我们的文档以达到这种效果,那将非常感激。 :)请发送PR给https://github.com/shazow/urllib3并添加自己作为贡献者!

答案 2 :(得分:2)

  

如图所示,很明显urlopen()返回一个由变量内容捕获的HTTP响应......

您所谓的content不是内容,而是一个类似文件的对象,您可以从中读取内容。 BeautifulSoup非常乐意接受这样的事情,但是出于调试目的而打印它并不是很有帮助。所以,让我们实际读取它的内容,以便更容易调试:

>>> response = connectBuilder.urlopen('GET', 'http://www.crummy.com/software/BeautifulSoup/')
>>> response
<urllib3.response.HTTPResponse object at 0x00000000032EC390>
>>> content = response.read()
>>> content
b''

这应该很清楚BeautifulSoup不是问题。但继续:

  

...但在将其传入Beautiful Soup之后,网络数据无法转换为Beautiful Soup对象(变量汤)。

是的。 soup.title为您提供None而不是提出AttributeError的事实是非常好的证据,但您可以直接测试它:

>>> type(soup)
bs4.BeautifulSoup

这绝对是BeautifulSoup对象。

当你传递BeautifulSoup一个空字符串时,你得到的确切内容将取决于它在封面下使用的解析器;如果它依赖于Python 3.x stdlib,那么你得到的是html节点,其中空head,空body,没有别的。因此,当您查找title节点时,没有一个节点,您将获得None


那么,你如何解决这个问题?

the documentation表示,你&#39;再利用&#34;最低级呼叫作出的请求,所以你需要指定所有的原始细节&#34;这些原始细节是什么?老实说,如果你还不知道,你不应该使用这种方法教你如何处理urllib3的底层细节,甚至在你不知道基础知识不会出现之前为你服务。

事实上,你根本不需要urllib3。只需使用Python附带的模块:

>>> # on Python 2.x, instead do: from urllib2 import urlopen 
>>> from urllib.request import urlopen
>>> r = urlopen('http://www.crummy.com/software/BeautifulSoup/')
>>> soup = BeautifulSoup(r)
>>> soup.title.text
'Beautiful Soup: We called him Tortoise because he taught us.'

答案 3 :(得分:0)

我漂亮的汤代码在一个环境(我的本地机器)中工作,并在另一个环境中返回一个空列表(ubuntu 14服务器)。

我已经解决了更改安装问题。 其他帖子中的详细信息:

Html parsing with Beautiful Soup returns empty list