【问题标题】:Passing web data into Beautiful Soup - Empty list将 Web 数据传递到 Beautiful Soup - 空列表
【发布时间】:2014-07-31 19:40:38
【问题描述】:

我重新检查了我的代码,并查看了打开 URL 以将网络数据传递到 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 之后,网络数据却没有' t 被转换为 Beautiful Soup 对象(变量汤)。你可以看到我已经尝试读取一些标签和文本,get_text() 返回一个空列表,这很奇怪。

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

【问题讨论】:

  • 它显然被转换为BeautifulSoup 对象——否则,soup.title 会引发异常而不是给你None。更好的判断方法是打印出type(soup)
  • 你的代码什么也没有,尝试打印 content.read()
  • 您手动构建池然后调用"the lowest level call for making a request" 是否有原因?
  • @abarnert 我明白了,谢谢。
  • @PadraicCunningham content.read() 给出 b''

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


【解决方案1】:

如果你只是想抓取页面,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'

【讨论】:

    【解决方案2】:

    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 对象是一个类似文件的对象,except 在这种情况下 urllib3 已经消耗了所有响应并为您解析它,因此没有任何东西可以@ 987654328@。这就像传递一个已经被读取的文件指针。另一方面,.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.request 的快速说明:

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

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

    【讨论】:

    • 非常感谢您的解释,我承认我不知道确切的内容预加载是什么。我是 Python 和相关项目的新手,虽然我知道更精确的操作通常需要 URL 参数,但我认为 urlopen 更基本并且是标准/首选方法。 :)
    • 别担心,您的经验对我来说是有用的反馈。 :)
    【解决方案3】:

    如图所示,很明显 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 之后,网络数据并没有被转换成 Beautiful Soup 对象(变量汤)。

    是的。 soup.title 给了你None 而不是提出AttributeError 的事实是很好的证据,但你可以直接测试它:

    >>> type(soup)
    bs4.BeautifulSoup
    

    这绝对是一个BeautifulSoup 对象。

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


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

    正如the documentation 所说,您正在使用“发出请求的最低级别调用,因此您需要指定所有原始详细信息。”那些原始细节是什么?老实说,如果你还不知道,你不应该使用这种方法 教你如何处理 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.'
    

    【讨论】:

    • 谢谢,但是当我尝试进一步解析时,我没有得到类似soup.find_all(True)和soup.get_text()的东西,所以我很困惑。
    • @user3885774:这就是我最后一段解释的内容:你可能有一个空汤,或者一个汤只有一个 html 节点和一个空的 headbody,但它确实没有没关系;没有有用的数据,那么谁在乎这种缺乏有用的数据究竟是如何表示的呢?
    • urllib3 实际上返回一个类似文件的对象,但默认情况下会使用它(这并不理想,正如我在下面的回答中提到的并打开了一个问题)。要解决此问题,请在请求参数中使用 preload_content=False。
    • @shazow:或者,更简单地说,只需使用 r.data,这是预加载内容所在的位置。或者,更简单地说,如果您不需要它,请不要使用 urllib3,因为它太复杂了,您无法在文档中找到您需要的东西……
    • @abarnert 或者给 urllib3 的作者反馈如何使它不太复杂,以便他可以修复它。 :) 或者更喜欢,来帮助改进它!
    【解决方案4】:

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

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

    Html parsing with Beautiful Soup returns empty list

    【讨论】:

    • 请注意 link-only answers 是不鼓励的,所以答案应该是寻找解决方案的终点(与另一个中途停留的参考相比,随着时间的推移往往会变得陈旧)。请考虑在此处添加独立的概要,并保留链接作为参考。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-27
    • 2021-09-07
    • 2017-06-30
    • 1970-01-01
    • 2018-11-14
    • 2018-02-02
    相关资源
    最近更新 更多