【问题标题】:How to convert raw javascript object to a dictionary?如何将原始 javascript 对象转换为字典?
【发布时间】:2014-07-24 12:31:52
【问题描述】:

当屏幕抓取某些网站时,我从<script> 标签中提取数据。
我得到的数据不是标准的JSON 格式。我不能使用json.loads()。

# from
js_obj = '{x:1, y:2, z:3}'

# to
py_obj = {'x':1, 'y':2, 'z':3}

目前,我使用regex 将原始数据转换为JSON 格式。
但是遇到复杂的数据结构,我感觉很糟糕。

你有更好的解决方案吗?

【问题讨论】:

  • 你要解析的数据有什么不规范的地方?
  • @HuuNguyen 我想将Plain old javascript data structure解析为python对象。
  • 哦,我没有看到 js_obj 的键周围没有引号。你的数据结构会变得多复杂?在不知道您要解决的案例的情况下,很难提出任何建议。
  • @HuuNguyen js_obj 可能是嵌套的
  • 已经有关于 SO 的类似问题:stackoverflow.com/a/10057449/384442 他们都没有提供任何现成的解决方案

标签: javascript python json web-scraping


【解决方案1】:

demjson.decode()

import demjson

# from
js_obj = '{x:1, y:2, z:3}'

# to
py_obj = demjson.decode(js_obj)

jsonnet.evaluate_snippet()

import json, _jsonnet

# from
js_obj = '{x:1, y:2, z:3}'

# to
py_obj = json.loads(_jsonnet.evaluate_snippet('snippet', js_obj))

ast.literal_eval()

import ast

# from
js_obj = "{'x':1, 'y':2, 'z':3}"

# to
py_obj = ast.literal_eval(js_obj)

【讨论】:

  • JSON 不支持圆形对象
  • demjson 和 jsonnet 的链接失效
  • demjson 给 Python 3 带来了问题,因为 Setuptools 已经删除了对 2to3 的支持。所以对于现在使用 python 3.X 的人来说,它可能不是一个有效的替代方案。
【解决方案2】:

使用json5

import json5

js_obj = '{x:1, y:2, z:3}'

py_obj = json5.loads(js_obj)

print(py_obj)

# output
# {'x': 1, 'y': 2, 'z': 3}

【讨论】:

  • 这是最好的一个:)
【解决方案3】:

今天下午我也遇到了同样的问题,我终于找到了一个很好的解决方案。那是JSON5。

JSON5 的语法更类似于原生 JavaScript,因此可以帮助你解析非标准的 JSON 对象。

您可能想签出pyjson5。

【讨论】:

  • 这是最快的库。我尝试使用 demjson,我的脚本运行了 10.5 秒。 Pyjson5在0.004s内完成这个任务。
  • @Inventor 你能发一个例子来说明它是如何工作的吗?
【解决方案4】:

如果系统上有node 可用,您可以要求它为您计算javascript 表达式,并打印字符串化结果。然后可以将生成的 JSON 提供给 json.loads:

def evaluate_javascript(s):
    """Evaluate and stringify a javascript expression in node.js, and convert the
    resulting JSON to a Python object"""
    node = Popen(['node', '-'], stdin=PIPE, stdout=PIPE)
    stdout, _ = node.communicate(f'console.log(JSON.stringify({s}))'.encode('utf8'))
    return json.loads(stdout.decode('utf8'))

【讨论】:

    【解决方案5】:

    不包括对象

    json.loads()

    • json.loads() 不接受undefined,你要改成null
    • json.loads() 仅接受双引号
      • {"foo": 1, "bar": null}

    如果您确定您的 javascript 代码在键名上只有双引号,请使用此选项。

    import json
    
    json_text = """{"foo": 1, "bar": undefined}"""
    json_text = re.sub(r'("\s*:\s*)undefined(\s*[,}])', '\\1null\\2', json_text)
    
    py_obj = json.loads(json_text)
    

    ast.literal_eval()

    • ast.literal_eval() 不接受undefined,你要改成None
    • ast.literal_eval() 不接受null,你要改成None
    • ast.literal_eval() 不接受true,你要改成True
    • ast.literal_eval() 不接受false,你要改成False
    • ast.literal_eval() 接受单引号和双引号
      • {"foo": 1, "bar": None} 或 {'foo': 1, 'bar': None}
    import ast
    
    js_obj = """{'foo': 1, 'bar': undefined}"""
    js_obj = re.sub(r'([\'\"]\s*:\s*)undefined(\s*[,}])', '\\1None\\2', js_obj)
    js_obj = re.sub(r'([\'\"]\s*:\s*)null(\s*[,}])', '\\1None\\2', js_obj)
    js_obj = re.sub(r'([\'\"]\s*:\s*)NaN(\s*[,}])', '\\1None\\2', js_obj)
    js_obj = re.sub(r'([\'\"]\s*:\s*)true(\s*[,}])', '\\1True\\2', js_obj)
    js_obj = re.sub(r'([\'\"]\s*:\s*)false(\s*[,}])', '\\1False\\2', js_obj)
    
    py_obj = ast.literal_eval(js_obj) 
    

    【讨论】:

      【解决方案6】:

      这可能不会在任何地方都有效,但首先,这是一个简单的正则表达式,它应该将键转换为带引号的字符串,以便您可以传递到 json.loads。或者这就是你已经在做的事情?

      In[70] : quote_keys_regex = r'([\{\s,])(\w+)(:)'
      
      In[71] : re.sub(quote_keys_regex, r'\1"\2"\3', js_obj)
      Out[71]: '{"x":1, "y":2, "z":3}'
      
      In[72] : js_obj_2 = '{x:1, y:2, z:{k:3,j:2}}'
      
      Int[73]: re.sub(quote_keys_regex, r'\1"\2"\3', js_obj_2)
      Out[73]: '{"x":1, "y":2, "z":{"k":3,"j":2}}'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-19
        • 1970-01-01
        • 1970-01-01
        • 2017-11-01
        • 1970-01-01
        相关资源
        最近更新 更多