在Python 3中使用urllib时出现socket资源警告

11 投票
2 回答
4610 浏览
提问于 2025-04-17 16:15

我正在使用一个叫做 urllib.request.urlopen() 的方法,从我想测试的网络服务获取数据。

这个方法会返回一个 HTTPResponse 对象,我接着用 read() 方法来读取响应的内容。

不过,我总是看到一个关于未关闭的 socket 的资源警告,这个警告来自 socket.py。

下面是相关的函数:

from urllib.request import Request, urlopen

def get_from_webservice(url):
    """ GET from the webservice  """
    req = Request(url, method="GET", headers=HEADERS)
    with urlopen(req) as rsp:
        body = rsp.read().decode('utf-8')
        return json.loads(body)

这是程序输出中出现的警告:

$ ./test/test_webservices.py
/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/socket.py:359: ResourceWarning: unclosed <socket.socket object, fd=5, family=30, type=1, proto=6>
self._sock = None
.s
----------------------------------------------------------------------
Ran 2 tests in 0.010s

OK (skipped=1)

如果我能对 HTTPResponse(或者请求 Request)做些什么,让它能干净地关闭这个 socket,我真的很想知道,因为这段代码是我单元测试的一部分;我不喜欢在任何地方忽略警告,尤其是在这里。

2 个回答

1

我也遇到过和urllib3一样的问题,所以我加了一个上下文管理器,这样可以自动关闭连接:

import urllib3

def get(addr, headers):
    """ this function will close the connection after a http request. """
    with urllib3.PoolManager() as conn:
        res = conn.request('GET', addr, headers=headers)
        if r.status == 200:
            return res.data
        else:
            raise ConnectionError(res.reason)

需要注意的是,urllib3是为了管理连接池而设计的,它会帮你保持连接的活跃。如果你的应用需要连续发起多个请求,比如多次调用后端API,这样做可以大大加快速度。

请查看urllib3的文档,了解连接池的相关内容,链接在这里:https://urllib3.readthedocs.io/en/1.5/pools.html

附注:你也可以使用requests库,虽然它在2019年时不是Python标准库的一部分,但它功能强大且易于使用:http://docs.python-requests.org/en/master/

5

我不知道这是否是正确的答案,但这确实是解决问题的一部分。

如果我在我的网络服务的响应中添加“connection: close”这个头信息,HTTPResponse对象似乎就能正常清理自己,而不会出现警告。

实际上,HTTP规范(http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html)中提到:

不支持持久连接的HTTP/1.1应用程序必须在每条消息中包含“close”连接选项。

所以问题出在服务器那边(也就是说,是我的错!)。如果你无法控制服务器发送的头信息,我也不知道该怎么办。

撰写回答