7

我重新检查了我的代码,并查看了打开 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() 返回了一个被变量 content 捕获的 HTTP 响应,它可以读取响应的状态是有道理的,但是在它被传递到 Beautiful Soup 之后,Web 数据没有得到转换变成一个美丽的汤对象(变量汤)。您可以看到我尝试读取一些标签和文本,get_text() 返回一个空列表,这很奇怪。

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

4

4 回答 4

15

如果你只是想抓取页面,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'
于 2014-07-31T20:08:36.367 回答
12

urllib3 返回一个 Response 对象,其中包含.data预加载的正文有效负载。

根据此处的顶级快速入门用法示例,我会执行以下操作:

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有朝一日将其设为默认值。也许很快有一天(我在这里打开了一个问题)。

--

关于.urlopenvs的快速说明.request

.urlopen假设您将负责对传递给请求的任何参数进行编码。在这种情况下使用它很好,.urlopen因为您没有向请求传递任何参数,但通常.request会为您完成所有额外的工作,因此更方便。

如果有人愿意为此改进我们的文档,那将不胜感激。:) 请将 PR 发送到https://github.com/shazow/urllib3并将自己添加为贡献者!

于 2014-07-31T21:05:50.200 回答
2

如图所示,很明显 urlopen() 返回了一个 HTTP 响应,该响应由变量 content 捕获……</p>

您调用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而不是提高 an的事实AttributeError是很好的证据,但你可以直接测试它:

>>> type(soup)
bs4.BeautifulSoup

那绝对是一个BeautifulSoup对象。

当您传递BeautifulSoup一个空字符串时,您返回的确切内容将取决于它在幕后使用的解析器;如果它依赖于 Python 3.x 标准库,您将得到一个html带有 emptyhead和 empty的节点,仅此body而已。所以,当你寻找一个title节点时,没有一个,你会得到None.


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

正如文档所说,您使用的是“发出请求的最低级别的调用,因此您需要指定所有原始详细信息。” 那些原始细节是什么?老实说,如果你还不知道,你不应该使用这种方法教你如何处理引擎盖下的细节,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.'
于 2014-07-31T19:56:49.350 回答
0

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

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

使用 Beautiful Soup 进行 Html 解析返回空列表

于 2015-07-24T19:45:09.657 回答