AttributeError: 'tuple' object has no attribute 'timeout' - urllib request in Python3

AttributeError: 'tuple' object has no attribute 'timeout' - urllib request in Python3

我正在使用 urllib 发出请求并返回一些数据:

queryURL = "https://hazards.fema.gov/gis/nfhl/rest/services/CSLF/Prelim_CSLF/MapServer/3/query"
params = urllib.parse.urlencode({'f': 'json', 'geometryType': 'esriGeometryPolygon', 'outFields': 'OBJECTID, SHAPE, CSLF_ID, Area_SF', 'returnGeometry': 'false'})

直到这里我才 运行 遇到任何问题:

req = urllib.request.urlretrieve(queryURL, params)

然后:

urllib.request.urlopen(req)

此时我收到一个错误 - AttributeError: 'tuple' object has no attribute 'timeout'. 我知道它返回一个不可变的元组,因为我格式化 params 变量的方式。我的问题是,如何解决这个问题,以便我可以开始查看我的结果?

jsonResult = json.load(response)

根据要求,这是回溯:

AttributeError                            Traceback (most recent call last) <ipython-input-68-cb3f46b2da76> in <module>()
----> 1 urllib.request.urlopen(req)

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\urllib\request.py in urlopen(url, data, timeout, cafile, capath, cadefault, context)
    221     else:
    222         opener = _opener
--> 223     return opener.open(url, data, timeout)
    224 
    225 def install_opener(opener):

C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\lib\urllib\request.py in open(self, fullurl, data, timeout)
    515                 req.data = data
    516 
--> 517         req.timeout = timeout
    518         protocol = req.type
    519 

AttributeError: 'tuple' object has no attribute 'timeout'

核心问题是这段代码没有任何意义:

req = urllib.request.urlretrieve(queryURL, params)
urllib.request.urlopen(req)

urlretrieve 为您发起网络请求,将结果保存在本地文件中,然后 returns "a tuple (filename, headers)".

您正在尝试将 (filename, headers) 对传递给 urlopen。但是 urlopen 不知道该怎么办;它需要 URL 字符串或 Request object,而 (filename, headers) 对两者都不是。

此外,您甚至想要它做什么也不清楚。您已经提取请求并将结果保存在文件中。为什么要再次获取相同的请求?

简单的答案是,如果您不需要,就不要使用 urlretrieve。 (特别是因为它已经被弃用了……)就这样做:

response = urllib.request.urlopen(queryURL, params)

此外,请注意我将 urlopen 的结果保存在一个变量中。如果您不这样做,您只是在发出请求并丢弃响应,这不是很有用。 jsonResult = json.load(response) 会给你一个 NameError,因为你从未创建任何名为 response.

的东西

但是,您的代码仍然(至少)存在另外两个问题。

首先,正如 urlencode 的文档所说:

If the resultant string is to be used as a data for POST … then it should be encoded to bytes, otherwise it would result in a TypeError.

但你没有那样做。

其次,如果你想发送url-encoded数据作为你的POSTbody,你需要手动设置Content-Typeheader这样说.

有些服务器即使你弄错了也会猜对,但这真的不是你应该依赖的东西。


最后,如果您按照 urllib.request 文档顶部的建议进行操作,所有这些都会变得容易得多:

See also: The Requests package is recommended for a higher-level HTTP client interface.

有了请求,整个事情就变成了:

queryURL = "https://hazards.fema.gov/gis/nfhl/rest/services/CSLF/Prelim_CSLF/MapServer/3/query"
params = {'f': 'json', 'geometryType': 'esriGeometryPolygon', 'outFields': 'OBJECTID, SHAPE, CSLF_ID, Area_SF', 'returnGeometry': 'false'}
jsonResult = requests.post(queryURL, data=params).json()