将 Web 数据传递到 Beautiful Soup - 空列表
-
26-12-2019 - |
题
我重新检查了我的代码,并查看了打开 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 响应,该响应由变量 content 捕获,它可以读取响应的状态,但在将其传递到 Beautiful Soup 后,Web 数据没有得到转换进入 Beautiful Soup 对象(变量 soup)。你可以看到我尝试读取一些标签和文本, get_text() 返回一个空列表,这很奇怪。
奇怪的是,当我通过 content.data 访问网络数据时,数据会显示出来,但它没有用,因为我无法使用 Beautiful Soup 来解析它。我的问题是什么?谢谢。
解决方案
如果你只是想抓取页面, 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'
其他提示
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
object 是一个类似文件的对象, 除了 在这种情况下,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
有一天默认。也许不久的将来(我在这里打开了一个问题).
--
快速说明 .urlopen
与 .request
:
.urlopen
假设您将负责对传递给请求的任何参数进行编码。在这种情况下使用就可以了 .urlopen
因为您没有向请求传递任何参数,但一般来说 .request
将为您完成所有额外的工作,因此更加方便。
如果有人愿意为此改进我们的文档,我们将不胜感激。:) 请将 PR 发送至 https://github.com/shazow/urllib3 并将您自己添加为贡献者!
如图所示,很明显 urlopen() 返回一个 HTTP 响应,该响应由变量 content 捕获......
你叫什么 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 后,Web 数据不会转换为 Beautiful Soup 对象(变量 soup)。
是的,它确实。事实是 soup.title
给你 None
而不是提出 AttributeError
是很好的证据,但你可以直接测试它:
>>> type(soup)
bs4.BeautifulSoup
这绝对是一个 BeautifulSoup
目的。
当你通过时 BeautifulSoup
一个空字符串,您返回的确切内容将取决于它在幕后使用哪个解析器;如果它依赖于 Python 3.x stdlib,你会得到的是 html
节点为空 head
, ,和空 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.'