尝试提取 json 时出现 JSONDecodeError

JSONDecodeError when trying to extract a json

我每次尝试在此 api 中提取 json 时都会收到此错误 Request_URL='https://freeserv.dukascopy.com/2.0/api/group=quotes&method=realtimeSentimentIndex&enabled=true&key=bsq3l3p5lc8w4s0c&type=swfx&jsonp=_callbacks____1kvynkpid'

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

import json
import requests
import pandas as pd

r = requests.get(Request_URL)
df = pd.DataFrame(r.json())

问题是返回的响应是 JSONP 格式。也就是说,它 JavaScript 由对函数的调用组成,该函数的参数是 JavaScript 结构 应该 是一个有效的 JSON 字符串,如果它有单引号,但不能保证它是)。部分看起来像:

_callbacks____1kvynkpid([{"id":"10012" ...])

所以我们需要先删除 JavaScript 调用,它是前导字符,包括第一个 ( 字符和最后一个 ):

import requests
import json

request_url = 'https://freeserv.dukascopy.com/2.0/api?group=quotes&method=realtimeSentimentIndex&enabled=true&key=bsq3l3p5lc8w4s0c&type=swfx&jsonp=_callbacks____1kvynkpid'

r = requests.get(request_url)
text = r.text
idx = text.index('(')
# skip everything up to and including opening '(' and then skip closing ')'
text = text[idx+1:-1]
print(json.loads(text))

打印:

[{'id': '10012', 'title': 'ESP.IDX/EUR', 'date': '1636925400000', 'long': '71.43', 'short': '28.57'}, {'id': '10104', 'title': 'AUS.IDX/AUD', 'date': '1636925400000', 'long': '70.59', 'short': '29.41'}, {'id': '10266', 'title': 'NLD.IDX/EUR', 'date': '1636925400000', 'long': '73.48', 'short': '26.52'},

... data too big too fully reproduce

 {'id': '82862', 'title': 'MAT/USD', 'date': '1636925400000', 'long': '70.27', 'short': '29.73'}, {'id': '82866', 'title': 'ENJ/USD', 'date': '1636925400000', 'long': '72.16', 'short': '27.84'}]

在这种情况下,解释为字符串的结构遵循 JSON 格式,因此我们能够使用 json.loads() 解析它。但是,如果 JavaScript 结构(部分)是:

[{'id':'10012'}]

这既合法 JavaScript 也合法 Python,但不合法 JSON 因为字符串必须用双引号括起来才能有效 JSON。但是因为它是合法的 Python,我们可以使用 ast.literal_eval:

import requests
import ast

request_url = 'https://freeserv.dukascopy.com/2.0/api?group=quotes&method=realtimeSentimentIndex&enabled=true&key=bsq3l3p5lc8w4s0c&type=swfx&jsonp=_callbacks____1kvynkpid'

r = requests.get(request_url)
text = r.text
idx = text.index('(')
# skip everything up to and including opening '(' and then skip closing ')'
text = text[idx+1:-1]
print(ast.literal_eval(text))

当然,对于目前的情况,json.loadsast.literal_eval 碰巧都有效。但是,如果 JavaScript 结构是:

[{id:'10012'}]

这是有效的 JavaScript 但是,唉,无效 Python 并且无法用 json.loadsast.literal_eval 解析。