使用 Pandas 读取 JSON 时出现“期望字符串或 Unicode”错误

6

我尝试读取一个Openstreetmaps API的输出JSON字符串,该字符串有效。

我将使用以下代码:

我正在使用以下代码:

import pandas as pd
import requests

# Links unten
minLat = 50.9549
minLon = 13.55232

# Rechts oben
maxLat = 51.1390
maxLon = 13.89873

osmrequest = {'data': '[out:json][timeout:25];(node["highway"="bus_stop"](%s,%s,%s,%s););out body;>;out skel qt;' % (minLat, minLon, maxLat, maxLon)}
osmurl = 'http://overpass-api.de/api/interpreter'
osm = requests.get(osmurl, params=osmrequest)

osmdata = osm.json()

osmdataframe = pd.read_json(osmdata)

出现以下错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-66-304b7fbfb645> in <module>()
----> 1 osmdataframe = pd.read_json(osmdata)

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in read_json(path_or_buf, orient, typ, dtype, convert_axes, convert_dates, keep_default_dates, numpy, precise_float, date_unit)
    196         obj = FrameParser(json, orient, dtype, convert_axes, convert_dates,
    197                           keep_default_dates, numpy, precise_float,
--> 198                           date_unit).parse()
    199 
    200     if typ == 'series' or obj is None:

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in parse(self)
    264 
    265         else:
--> 266             self._parse_no_numpy()
    267 
    268         if self.obj is None:

/Users/paul/anaconda/lib/python2.7/site-packages/pandas/io/json.pyc in _parse_no_numpy(self)
    481         if orient == "columns":
    482             self.obj = DataFrame(
--> 483                 loads(json, precise_float=self.precise_float), dtype=None)
    484         elif orient == "split":
    485             decoded = dict((str(k), v)

TypeError: Expected String or Unicode

如何修改请求或Pandas的read_json来避免错误?顺便问一下,问题出在哪里?

1个回答

13

如果你将JSON字符串打印到文件中,

content = osm.read()
with open('/tmp/out', 'w') as f:
    f.write(content)

你会看到类似这样的内容:

{
  "version": 0.6,
  "generator": "Overpass API",
  "osm3s": {
    "timestamp_osm_base": "2014-07-20T07:52:02Z",
    "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
  },
  "elements": [

{
  "type": "node",
  "id": 536694,
  "lat": 50.9849256,
  "lon": 13.6821776,
  "tags": {
    "highway": "bus_stop",
    "name": "Niederhäslich Bergmannsweg"
  }
},
...]}
如果将JSON字符串转换为Python对象,它将是一个字典,其中elements键是字典列表。绝大部分数据都在这个字典列表中。
这个JSON字符串不直接可转换为Pandas对象。什么将是索引,什么将是列呢?肯定你不想让[u'elements', u'version', u'osm3s', u'generator']成为列,因为几乎所有的信息都在elements字典列表中。
但如果你想让DataFrame只包含elements字典列表中的数据,那么你需要指定,因为Pandas无法替你做出这个假设。
进一步复杂化的是,elements中的每个字典都是一个嵌套字典。考虑elements中的第一个字典:
{
  "type": "node",
  "id": 536694,
  "lat": 50.9849256,
  "lon": 13.6821776,
  "tags": {
    "highway": "bus_stop",
    "name": "Niederhäslich Bergmannsweg"
  }
}

使用['lat', 'lon', 'type', 'id', 'tags']作为列是否合适?这似乎是可行的,但tags列最终会变成字典列。通常这没什么用处。也许最好的方式是将tags字典内部的键(keys)转换为列。我们可以这样做,但需要自己编写代码,因为Pandas不知道我们想要这样做。


import pandas as pd
import requests
# Links unten
minLat = 50.9549
minLon = 13.55232

# Rechts oben
maxLat = 51.1390
maxLon = 13.89873

osmrequest = {'data': '[out:json][timeout:25];(node["highway"="bus_stop"](%s,%s,%s,%s););out body;>;out skel qt;' % (minLat, minLon, maxLat, maxLon)}
osmurl = 'http://overpass-api.de/api/interpreter'
osm = requests.get(osmurl, params=osmrequest)

osmdata = osm.json()
osmdata = osmdata['elements']
for dct in osmdata:
    for key, val in dct['tags'].iteritems():
        dct[key] = val
    del dct['tags']

osmdataframe = pd.DataFrame(osmdata)
print(osmdataframe[['lat', 'lon', 'name']].head())
产量
         lat        lon                        name
0  50.984926  13.682178  Niederhäslich Bergmannsweg
1  51.123623  13.782789                Sagarder Weg
2  51.065752  13.895734     Weißig, Einkaufszentrum
3  51.007140  13.698498          Stuttgarter Straße
4  51.010199  13.701411          Heilbronner Straße

1
太好了,解释得非常清楚!我完全理解了,但是自己无法解决这个谜题。谢谢!在这一行 osmdata = json.loads(osm.read()) 中可能有些不正确的地方,因为我得到了这个错误:AttributeError: 'Response' object has no attribute 'read' - Balzer82
它必须是 osmdata = json.loads(osm.content) - Balzer82
@Balzer82:我的requests版本太老了,API自那时起已经发生了变化。我已经更新了我的requests并将编辑上面的代码。 - unutbu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接