ValueError: 使用ast.literal_eval时出现格式错误的字符串

21

我正在使用循环获取JSON API,以下是我的循环内容:

response_item = requests.request('GET',url_item,params=None,verify=False)
response_item = json.loads(response_item.text)
response_item = ast.literal_eval(json.dumps(response_item, ensure_ascii=False).encode('utf8'))

我扫描了大约45000个JSON对象,在每次迭代中都生成了“url_item”变量。每个对象都是相同的,我可能会获取大约7000个对象,但在达到第7064个对象时出现以下错误:

Traceback (most recent call last):
  File "C:\Python27\tools\api_item.py", line 47, in <module>
    response_item = ast.literal_eval(json.dumps(response_item, ensure_ascii=False).encode('utf8'))
  File "C:\Python27\lib\ast.py", line 80, in literal_eval
    return _convert(node_or_string)
  File "C:\Python27\lib\ast.py", line 63, in _convert
    in zip(node.keys, node.values))
  File "C:\Python27\lib\ast.py", line 62, in <genexpr>
    return dict((_convert(k), _convert(v)) for k, v
  File "C:\Python27\lib\ast.py", line 63, in _convert
    in zip(node.keys, node.values))
  File "C:\Python27\lib\ast.py", line 62, in <genexpr>
    return dict((_convert(k), _convert(v)) for k, v
  File "C:\Python27\lib\ast.py", line 79, in _convert
    raise ValueError('malformed string')
ValueError: malformed string

我曾经打印第二个和第三个“response_item”。当然,在这种情况下,由于我在 json.load 之前发生了错误,所以第三个没有显示。下面是我在 json.load 后打印的内容:

{u'restrictions': [], u'name': u'Sac \xe0 dos de base', u'level': 0, u'rarity': u'Basic', u'vendor_value': 11, u'details': {u'no_sell_or_sort': False, u'size': 20}, u'game_types': [u'Activity', u'Wvw', u'Dungeon', u'Pve'], u'flags': [u'NoSell', u'SoulbindOnAcquire', u'SoulBindOnUse'], u'icon': u'https://render.guildwars2.com/file/80E36806385691D4C0910817EF2A6C2006AEE353/61755.png', u'type': u'Bag', u'id': 8932, u'description': u'Un sac de 20 emplacements pour les personnages d\xe9butants.'}

在这个之前我得到的每一个项目都有相同的类型和格式,除了第7064个之外,我没有任何错误!

谢谢你的帮助!


1
你为什么要在json.dumps()的输出上使用ast.literal_eval()呢? - Martijn Pieters
1
推荐使用output = json.dumps(json.load(fdobject))而不是ast.literal_eval吗? - S.K. Venkat
2个回答

47

您不应该在JSON数据上使用 ast.literal_eval()。JSON和Python字面值看起来可能相同,但它们实际上是截然不同的。

在这种情况下,您的数据包含一个布尔标志,在JSON中设置为 false。正确的Python布尔值使用标题大小写,因此是 False

>>> import json, ast
>>> s = '{"no_sell_or_sort": false, "size": 20}'
>>> json.loads(s)
{u'no_sell_or_sort': False, u'size': 20}
>>> ast.literal_eval(s)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/ast.py", line 80, in literal_eval
    return _convert(node_or_string)
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/ast.py", line 63, in _convert
    in zip(node.keys, node.values))
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/ast.py", line 62, in <genexpr>
    return dict((_convert(k), _convert(v)) for k, v
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/ast.py", line 79, in _convert
    raise ValueError('malformed string')
ValueError: malformed string

其他区别包括使用null而不是None,以及在对Python 2来说看起来像是普通(字节)字符串的东西中使用Unicode转义序列,当转义非BMP代码点时使用UTF-16代理。

使用json.loads()加载数据,而不是使用ast.literal_eval()。它不仅可以很好地处理正确的JSON,而且速度更快。

在您的情况下,似乎您正在使用json.dumps()然后再使用ast.literal_eval()加载数据。这一步是不必要的,因为你已经有了一个Python对象。

换句话说,这行代码:

response_item = ast.literal_eval(json.dumps(response_item, ensure_ascii=False).encode('utf8'))

最好的情况下是多余的,最坏的情况下是非常错误的。重新将response_item编码为JSON字符串并不能产生可以解释为Python字面量的东西。


而且更安全,我可以补充一下。 - Kroltan
2
@Kroltan:ast.literal_eval()是非常安全的。它将字符串解析为抽象语法树,然后只将字面值(字符串、列表、数字等)映射到Python对象。其他所有内容都会引发异常。 - Martijn Pieters
2
我已按照您的要求删除了带有ast.literal_eval()的行。现在我没有错误,但是我遇到了一些Unicode字符“u'”,我尝试了很多方法来移除这些字符,因为它们被插入到数据库中。这就是为什么我添加了带有ast.的那一行的原因。 - Aurélien
@Aurélien:你需要修复那段代码,问题出在那里,而不是你如何加载数据的方式。 - Martijn Pieters
1
@Aurélien:也许你可以发布一个新的问题,并附上如何将数据插入数据库的示例。u'...'对象是Python Unicode文本对象,前缀用于区分Python字节串类型。 - Martijn Pieters
显示剩余2条评论

-4

如果您正在使用ast.literal_eval,那么它是安全的,可以防止SQL注入。

因为当插入不需要的字符时,它会显示语法错误,从而防止注入。


这与SQL注入无关。而SQL注入依赖于字符串内容,而不是类型。 - Martijn Pieters
1
请注意,审查者们:这段内容不应该被标记为“非常低质量”,因为“非常低质量”队列不是用来处理技术错误的——那就需要采取投票和评论的方式。这段内容已经被投票降分并有评论解释了其错误之处,所以无需进一步处理。 - EJoshuaS - Stand with Ukraine
在审核队列中,点击看起来还不错。 - 10 Rep

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