最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】Python中用json.loads去解析字符串出错:ValueError: Expecting property name: line 1 column 51 (char 51)

JSON crifan 19363浏览 0评论

【问题】

想要给BlogsToWordpress添加支持新版百度空间,然后通过:

http://hi.baidu.com/qcmt/data/cmtlist?count=10&thread_id_enc=c260a3d575252069fb5768ed&orderby_type=1&favor=2&start=0&qing_request_source=&type=smblog

去获得对应的帖子:

http://hi.baidu.com/hispace/item/c260a3d575252069fb5768ed

中的评论数据respCmtJson:

{"errorNo" : "0","errorMsg" : "success","data": [ {total_count : ‘129’,real_ret_count : ”,items : [{reply_id_enc : ‘3dc74bcd041d630ac710b2d6’,thread_id_enc : ‘8625437697c2ac29d6a89c75′,parent_id_enc : ’98ce38cba94a9a0cac092fe2’,reply_count : ‘0’,score : ‘0’,like_count : ‘0’,dislike_count : ‘0’,favor : ‘0’,is_top : ‘0’,cdatetime : ‘1342690951’,portrait : ’57bd31303330353530343434000b’,un : ‘1030550444’,area : ”,title : ”,reserved1 : ‘0’,content : ‘0’,replyee_portrait : ”,replyee_qurl : ”,replyee_name : ”,avatar : ‘http://hiphotos.baidu.com/space/scrop=40;q=100/sign=1f6d28a2b6003af349e49b3f4517f26c/7af40ad162d9f2d37e92ec44a9ec8a136227cc9e.jpg’,qurl : ‘\/go\/check?portrait=57bd66616b65000b’},{reply_id_enc : ‘fed96a6fca35820aa1cf0fd4’,thread_id_enc : ‘8625437697c2ac29d6a89c75′,parent_id_enc : ’98ce38cba94a9a0cac092fe2’,reply_count : ‘0’,score : ‘0’,like_count : ‘0’,dislike_count : ‘0’,favor : ‘0’,is_top : ‘0’,cdatetime : ‘1342690163’,portrait : ‘9d746c616c61383134400a’,un : ‘lala814’,area : ”,title : ”,reserved1 : ‘0’,content : ‘1234’,replyee_portrait : ”,replyee_qurl : ”,replyee_name : ”,avatar : ‘http://hiphotos.baidu.com/space/scrop=40;q=100/sign=a281b92f8c5494ee837c48465dc8d4ce/b7003af33a87e9506475cbfc10385343fbf2b43b.jpg’,qurl : ‘\/go\/check?portrait=9d7466616b65400a’},{reply_id_enc : ‘1ed3b252dde2fbaaacc857a8’,thread_id_enc : ‘8625437697c2ac29d6a89c75′,parent_id_enc : ’98ce38cba94a9a0cac092fe2’,reply_count : ‘0’,score : ‘0’,like_count : ‘0’,dislike_count : ‘0’,favor : ‘0’,is_top : ‘0’,cdatetime : ‘1342687361’,portrait : ‘369971716271627162717162cf17’,un : ‘追星赶花’,area : ”,title : ”,reserved1 : ‘0’,content : ‘直接给种子你懂得’,replyee_portrait : ”,replyee_qurl : ”,replyee_name : ”,avatar : ‘http://hiphotos.baidu.com/space/scrop=40;q=100/sign=26c32d9a0cd79123e4bed32bdd096db2/503d269759ee3d6db9b8fa5943166d224f4ade22.jpg’,qurl : ‘\/go\/check?portrait=369966616b65cf17’},{reply_id_enc : ‘9d3cdc855a7b0eddd0f8cd44’,thread_id_enc : ‘8625437697c2ac29d6a89c75′,parent_id_enc : ’98ce38cba94a9a0cac092fe2’,reply_count : ‘0’,score : ‘0’,like_count : ‘0’,dislike_count : ‘0’,favor : ‘0’,is_top : ‘0’,cdatetime : ‘1342687078’,portrait : ‘11316565646c6573734418’,un : ‘eedless’,area : ”,title : ”,reserved1 : ‘0’,content : ‘还有什么好看的电影么`’,replyee_portrait : ”,replyee_qurl : ”,replyee_name : ”,avatar : ‘http://hiphotos.baidu.com/space/scrop=40;q=100/sign=39e0d6482f2eb938e8333dada55fb105/8644ebf81a4c510fb4fe37b36059252dd42aa565.jpg’,qurl : ‘\/go\/check?portrait=113166616b654418’}]} ]}

然后用json去解析:

cmtDict = json.loads(respCmtJson);

结果出错:

ValueError: Expecting property name: line 1 column 51 (char 51)

其中,此处不存在编码的问题,因为respCmtJson是UTF-8的。

【解决过程】

1.之前就遇到过类似json字符串解析成dict变量的问题。

后来是分别用eval,或ast.literal_eval,或json.loads而解决。

此处所以就又去试试eval和ast.literal_eval,结果都不行,都会出现解析错误:

(1)用

cmtDict = ast.literal_eval(respCmtJson);

出现:

ValueError: malformed string

(2)用

cmtDict = eval(respCmtJson);

出现:

NameError: name ‘total_count’ is not defined

所以都还是不行。

2.参考Python dict to JSON via json.loads:去试了试json.dumps,结果很明显,只是从utf-8的str转换为unicode的字符而已,然后用转换后的unicode字符再去json.loads,也还是同样出错。

3.由Issue5067想到,去把上述中单引号,换成双引号:

respCmtJson = respCmtJson.replace("'", "\"");

结果,让人失望,问题依旧。

4.专门去查了下那个错误位置51,发现是

"data": [ { ……} ]

中的第一个中括号之后,大括号之前,所以,看起来感觉是,json对于此处两个中括号之间,即列表变量中的值,不支持是dict类型的变量?

5.看到:Handling lazy JSON in Python – ‘Expecting property name’,才发现,原来上述猜测是错的。

问题根源在于,此处,对于json中的key和value来说,正常的话,是需要都用引号括起来的,而此处"data"之后的,第一个key是total_count,就没有用引号括起来,所以json才报错,不支持的。

类似地,后面的很多个key,比如real_ret_count,items,reply_id_enc等,全部都是没有引号括起来的,所以都会出错的。

然后就参考帖子的做法,去手动用正则表达式,把字符串处理为标准的json字符,就可以正确解析了。

如下是可以工作的代码:

        respCmtJson = re.sub(r"(,?)(\w+?)\s+?:", r"\1'\2' :", respCmtJson);
        respCmtJson = respCmtJson.replace("'", "\"");
        logging.debug("after filter, respCmtJson=%s", respCmtJson);
        
        cmtDict = json.loads(respCmtJson);
        logging.debug("cmtDict=%s", cmtDict);

【总结】

解析json字符串位dict变量之前,的确要先确保json字符串是否符合标准。

此处就是由于json的key没有双引号,导致json解析错误的。

解决办法是,用正则表达式将非法的json字符串处理为合法的,符合json标准的字符串,然后在用json.loads,就可以正确解析了。

转载请注明:在路上 » 【已解决】Python中用json.loads去解析字符串出错:ValueError: Expecting property name: line 1 column 51 (char 51)

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (7)

  1. 谢谢楼主
    小艾一万7年前 (2017-09-10)回复
  2. http://stackoverflow.com/questions/9156417/valid-json-giving-jsondecodeerror-expecting-delimiter 你试试直接在字符串前面加上r试试 https://docs.python.org/3/reference/lexical_analysis.html
    Pegasus9年前 (2016-06-30)回复
  3. 非常感谢您的这篇文章
    杀手9年前 (2015-10-09)回复
  4. 请问,你是怎么找到错误位置的呢?我现在做的东西跟你的很像,但是运行的时候就出现这样的错误:ValueError: Expecting ',' delimiter: line 1 column 1461 (char 1460) 因为抓取到的网页数据非常庞大,有什么好的方法找到出错位置呢? 另外,你能跟我解释一下你写的下面代码的意思吗?谢谢! respCmtJson = re.sub(r"(,?)(\w+?)\s+?:", r"\1'\2' :", respCmtJson); respCmtJson = respCmtJson.replace("'", "\""); logging.debug("after filter, respCmtJson=%s", respCmtJson); cmtDict = json.loads(respCmtJson); logging.debug("cmtDict=%s", cmtDict);
    luguiqiao11年前 (2013-10-09)回复
    • 是借助于错误提示信息,一点点找到错误位置的: 你的错误提示中,都写明了,是第一行,第1461列出错的,所以你去: 1.把所要处理的字符串,拷贝到Notepad++中 注意是不要手动去换行,要保证你所处理的字符串,本身是什么样的, 然后原样粘贴到Notepad++中 =》为了保证行号和列号是和错误信息中匹配 2.确保鼠标是处在(错误所在的)第一行的, 3.然后打开Notepad++中的跳转功能(方法1:搜索->行定位,方法2:Ctrl+G) 4.然后选择"偏移量",然后在"目标位置"中输入1461,点击"定位",即可跳转到对应的: 第一行的1461列的位置 即为错误的位置。 剩下的,就只能靠你自己,去看看该处为何出错, 去利用你所掌握的json的语法,去检查一下,到底哪里的写法,和标准的json语法不一致,而导致出错的。
      crifan11年前 (2013-10-09)回复
      • 好的,谢谢!
        luguiqiao11年前 (2013-10-09)回复
    • respCmtJson = re.sub(r”(,?)(\w+?)\s+?:”, r”\1′\2′ :”, respCmtJson); 把所有的,没有被引号括起来的key,加上单引号 即把: total_count : '129' ,real_ret_count : '' 等 变成 'total_count' : '129' ,'real_ret_count' : '' respCmtJson = respCmtJson.replace(“‘”, “\”"); 把所有的单引号,变成双引号 =》 上面两行,所要解决的问题是: 消除了,json中错误的写法: (1)key没有被(双引号)括起来 (2)所用引号不是单引号 对应的背景是: (1)json中key,必须被双引号括起来 (2)json中要求必须都是双引号 logging.debug(“after filter, respCmtJson=%s”, respCmtJson); cmtDict = json.loads(respCmtJson); 此时,处理后的json字符串,已经是合法的了 所以直接调用json.loads去转换为对应的(字典,列表等类型的)变量,即可。 logging.debug(“cmtDict=%s”, cmtDict); 另外: 感兴趣的,可以去看我写的教程: JSON详解
      crifan11年前 (2013-10-09)回复
89 queries in 0.238 seconds, using 22.16MB memory