【问题标题】:TypeError: Object of type dict_values is not JSON serializableTypeError:dict_values 类型的对象不是 JSON 可序列化的
【发布时间】:2021-07-04 11:54:50
【问题描述】:

我一直在使用 Python 和 Requests 模块按位置获取所有 Irelands Covid 数据。 我遇到了一个问题,我从 API 调用中获取数据的 dict 无法转换为 JSON,我想在 Pandas Dataframe 上显示它

现在,不幸的是,API 的网站有限,我必须为每个位置使用 for 循环来获取该区域的数据。然后当我这样做时,我将其推送到字典 thisdict["of that locations name"]

然后我尝试将它创建的所有 25 个字典转换为 JSON

toJSON = json.dumps(thisdict.values())
data = json.loads(toJSON)

现在这是我得到错误的地方 但是,如果我使用我创建的其中一个位置的位置,它将起作用,但我想要所有位置。这可能吗

toJSON = json.dumps(thisdict["Dublin"])
data = json.loads(toJSON)

我试过了

toJSON = json.dumps(*thisdict)
data = json.loads(toJSON)

toJSON = json.dumps(list(thisdict.values())
data = json.loads(toJSON)

我在这里找到的 https://markhneedham.com/blog/2017/03/19/python-3-typeerror-object-type-dict_values-not-json-serializable/

所有代码都在这个链接中 https://replit.com/@MrGallen/GetC19ApiToCSVEveryCounty#main.py

 # to handle  data retrieval
import requests
# to manage json data
import json
# for pandas dataframes
import pandas as pd

counties = ["Carlow", "Cavan", "Clare", "Cork", "Donegal", "Dublin", "Galway", "Kerry", "Kildare", "Kilkenny", "Laois", "Leitrim", "Limerick", "Longford", "Louth", "Mayo", "Meath", "Monaghan", "Offaly", "Roscommon", "Sligo", "Tipperary", "Waterford", "Westmeath", "Wexford", "Wicklow"]
thisdict = {}
for county in counties:
  url = "https://services1.arcgis.com/eNO7HHeQ3rUcBllm/arcgis/rest/services/Covid19CountyStatisticsHPSCIreland/FeatureServer/0/query?where=CountyName%20%3D%20'"+county+"'&outFields=CountyName,PopulationCensus16,TimeStamp,ConfirmedCovidCases,PopulationProportionCovidCases,ConfirmedCovidDeaths,ConfirmedCovidRecovered&returnGeometry=false&outSR=4326&f=json"
  r = requests.get(url, stream=True)
  #info = r.headers
  #print(info)
  r = r.json()
  r = r["features"]
  thisdict[county] = r

# decode json data into a dict object
toJSON = json.dumps(thisdict)
data = json.loads(toJSON)
# in this dataset, the data to extract is under 'features'
with open("sample.json", "w") as outfile: 
    json.dump(data, outfile)

df = pd.json_normalize(data)
print(df.head(10))

# Select a number of columns - all rows
CD = df[['attributes.CountyName', 'attributes.TimeStamp', 'attributes.ConfirmedCovidCases']]

print(CD) # DataFrame

【问题讨论】:

    标签: python json pandas dictionary python-requests


    【解决方案1】:

    这里真正的问题是您将字典放入的一般格式。当您只需要一个大的县属性列表时,您最终会不必要地嵌套多个列表。

    response.json()["features"] 是一个属性列表。例如,Carlow 返回该县的 407 个属性列表。因此,在您的 thisdict 中,您最终会得到一个县键的字典,其值都是属性列表。

    然后,您尝试获取该 dict 的 dict_values,这(如果您将 dict_values 转换为列表)会产生不必要的属性列表列表。

    这实际上是有效的,就(反)序列化进/出 JSON 而言,例如这不会引发序列化异常:

    toJSON = json.dumps(list(thisdict.values()))
    data = json.loads(toJSON)
    

    但是,稍后当您尝试将此 dicts 列表传递给 pandas.json_normalize() 时会遇到问题:

    Traceback (most recent call last):
      File "/home/dephekt/pandas/main.py", line 25, in <module>
        df = pd.json_normalize(data)
      File "/home/dephekt/pandas/.venv/lib/python3.8/site-packages/pandas/io/json/_normalize.py", line 270, in _json_normalize
        if any([isinstance(x, dict) for x in y.values()] for y in data):
      File "/home/dephekt/pandas/.venv/lib/python3.8/site-packages/pandas/io/json/_normalize.py", line 270, in <genexpr>
        if any([isinstance(x, dict) for x in y.values()] for y in data):
    AttributeError: 'list' object has no attribute 'values'
    

    您可以在回溯中看到它期望数据是字典列表或字典。它需要for y in data,然后尝试调用y.values(),假设y 是一个字典,但由于在这种情况下它是一个嵌套在一个列表中的列表并且一个列表没有values() 方法,它会抛出一个AttributeError。

    请考虑以下代码:

    import json
    import logging
    
    import pandas as pd
    
    import requests
    
    logging.basicConfig(level=logging.DEBUG)
    
    counties = [
        "Carlow",
        "Cavan",
        "Clare",
        "Cork",
        "Donegal",
        "Dublin",
        "Galway",
        "Kerry",
        "Kildare",
        "Kilkenny",
        "Laois",
        "Leitrim",
        "Limerick",
        "Longford",
        "Louth",
        "Mayo",
        "Meath",
        "Monaghan",
        "Offaly",
        "Roscommon",
        "Sligo",
        "Tipperary",
        "Waterford",
        "Westmeath",
        "Wexford",
        "Wicklow",
    ]
    
    results = []
    s = requests.Session()
    
    for county in counties:
        url = f"https://services1.arcgis.com/eNO7HHeQ3rUcBllm/arcgis/rest/services/Covid19CountyStatisticsHPSCIreland/FeatureServer/0/query"
        fields = "CountyName,PopulationCensus16,TimeStamp,ConfirmedCovidCases,PopulationProportionCovidCases,ConfirmedCovidDeaths,ConfirmedCovidRecovered"
        params = {
            "where": f"CountyName='{county}'",
            "outFields": fields,
            "returnGeometry": False,
            "outSR": 4326,
            "f": "json",
        }
        response = s.get(url, params=params, timeout=10)
        response.raise_for_status()
    
        features = response.json().get("features")
    
        for feature in features:
            results.append(feature)
    
    with open("sample.json", "w") as outfile:
        outfile.write(json.dumps(results))
    
    df = pd.json_normalize(results)
    print(df.head(10))
    
    CD = df[["attributes.CountyName", "attributes.TimeStamp", "attributes.ConfirmedCovidCases"]]
    
    print(CD)
    

    我在这里做的是创建一个请求会话(有关更多信息,请参见下文),然后对于每个县的响应,我们从 features 键中获取属性列表,然后迭代该列表并将各个属性放入我们的结果列表。

    你最终得到的是所有县的所有属性字典的一大列表。

    然后,我们避免了一些不必要的体操,你正在做 json.dumps() 只是然后 json.loads() 回到你已经拥有的格式,例如:

    toJSON = json.dumps(thisdict)
    data = json.loads(toJSON)
    

    在这里,data 最终与 thisdict 相同,没有理由将其反序列化回字典。您只需转储结果即可将它们写入文件。对于您的 pandas 参数,您可以传递 thisdict(或者在我的示例中,results 这是一个列表)。

    这一切都以这个作为输出结束:

      attributes.CountyName  ...  attributes.ConfirmedCovidRecovered
    0                Carlow  ...                                None
    1                Carlow  ...                                None
    2                Carlow  ...                                None
    3                Carlow  ...                                None
    4                Carlow  ...                                None
    5                Carlow  ...                                None
    6                Carlow  ...                                None
    7                Carlow  ...                                None
    8                Carlow  ...                                None
    9                Carlow  ...                                None
    
    [10 rows x 7 columns]
    
          attributes.CountyName  ...  attributes.ConfirmedCovidCases
    0                    Carlow  ...                               0
    1                    Carlow  ...                               0
    2                    Carlow  ...                               0
    3                    Carlow  ...                               0
    4                    Carlow  ...                               0
    ...                     ...  ...                             ...
    10577               Wicklow  ...                            4460
    10578               Wicklow  ...                            4473
    10579               Wicklow  ...                            4483
    10580               Wicklow  ...                            4491
    10581               Wicklow  ...                            4497
    
    [10582 rows x 3 columns]
    

    如果格式不完全符合您的要求,您可以在将其发送到您的 pandas 数据框之前更多地使用该格式,但希望这可以为您提供一个很好的示例。

    由于您在循环中发出一堆请求,因此我创建了一个requests.Session() 对象,因为它利用了 urllib3 的连接池并为每个 GET 请求重用相同的底层 TCP 连接,从而具有更好的性能。这样您就不会为每个循环执行重新协商一个新的 TCP 连接。它建立一个连接并通过该连接发送所有 26 个请求,而不是 26 个 TCP 连接,每个连接发送一个微小的请求。

    我还将您的请求查询参数与实际的 URL 端点分开,只是为了我自己的理智。您可以按照您之前的方式创建 URL,但是如果您需要更改这些参数的值,这确实为您提供了更多的灵活性。它也适用于您的长 URL,我只是觉得它很笨拙。

    【讨论】:

    • 非常感谢,正是我想要的和很好的解释。非常感谢
    • 很高兴听到这个消息。如果可以的话,请将其标记为已接受的答案并投赞成票:) 我正在尝试在 SO 上建立一些代表,因为我只有几天的帐户。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-11
    • 2021-12-10
    • 1970-01-01
    • 2021-09-27
    • 2019-09-04
    • 2019-01-12
    • 2019-03-27
    相关资源
    最近更新 更多