最近尝试对b站做数据分析,自己边学边造轮子。b站的 api 返回的格式都是json,在对数据进行格式转换的过程中遇到一个令人哭笑不得的问题,特此记录下来。
0x01
首先获得b站的api。这个很简单,浏览器打开b站网页,开发者工具->网络,然后过滤一下URL就能找到。
我先把get到的数据先保存到文件中,以便进一步分析json中包含哪些内容
class="highlighter-hljs hljs language-python hljs-line-numbers">textdata = requests.get(api, headers=headers).text jsonfile.write(textdata)
但这样做保存的json数据没有缩进,于是改为
class="highlighter-hljs hljs language-python hljs-line-numbers">jsondata = requests.get(api, headers=headers).json json.dump(jsondata, jsonfile, indent=4)
这里错将json作为了属性而不是方法,导致了接下来一系列错误!
报错:TypeError: Object of type 'method' is not JSON serializable
没注意到 traceback 中的 method 关键字,随手在网上一搜,还以为是字典数据中包含byte、int、float、datetime等类型数据,导致json.dump无法进行序列化,遂 copy 了一下代码。
【参考:解决:TypeError: Object of type xxx is not JSON serializable - 简书 (jianshu.com)】
import json
class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
print("MyEncoder-datetime.datetime")
return obj.strftime("%Y-%m-%d %H:%M:%S")
if isinstance(obj, bytes):
return str(obj, encoding='utf-8')
if isinstance(obj, int):
return int(obj)
elif isinstance(obj, float):
return float(obj)
#elif isinstance(obj, array):
# return obj.tolist()
else:
return super(MyEncoder, self).default(obj)
然后json.dump传入cls=MyEncoder参数,没想到继续报错:
raise TypeError(f'Object of type {o.__class__.__name__} '
这时候我天真的以为是因为字典中依然存在某些无法转换格式的数据,于是打算新建一个字典提取所需数据,再写入文件:
myjson = {}
for key in dictkey:
'''dictkey是所需提取的键列表'''
myjson[key] = jsondata['data'][key]
json.dump(myjson, jsonfile, indent=4)
报错:TypeError: 'method' object is not subscriptable
#这时候我还没意识到方向错了
我甚至怀疑是不是不能用变量做 key 值来插入字典的属性,但不应该啊。然后给新字典设置默认值 myjson.setdefault(key, 0),结果不起作用
然后用update方法:
class="highlighter-hljs hljs language-python hljs-line-numbers">myjson.update(key, jsondata['data'][key]) #TypeError: update expected at most 1 arguments, got 2
这里是单纯的方法使用错误,update方法传入的参数是一个字典,而不是键和值,可以传入两个字典,那么相当于调用了两次 update() 方法。
怎么想怎么不对劲,查阅资料发现字典在被赋值时完全可以用变量做键。相反,用于赋值或打印时,如果 key 对应的属性不存在,则会抛出 KeyError;如果 key 不是字符串,则可能抛出 TypeError。不过这些情况在这里都不成立。
直到这时我才回想起之前的 Traceback 中的 method,好家伙,原来是最开始漏写了小括号……
【参考】
- Python 在字典中插入会导致键错误吗?_Python_Exception Handling_Keyerror - 多多扣 (duoduokou.com)
- Python之字典添加元素_叫我王员外就行的博客-CSDN博客_python字典添加元素
- 理解 Python 语言中的 defaultdict - 团子的小窝 (kodango.com)
0x02
过程中我想到能否直接用dict()来将字符串转为字典呢?答案是不能。
报错:ValueError: dictionary update sequence element #0 has length 1; 2 is required
不过除了 json.dumps 外,eval()函数也能将字符串转为字典。
class="highlighter-hljs hljs language-python hljs-line-numbers">textdata = requests.get(api, headers=headers).text mydict = eval(textdata)
但这样做还有一个问题,eval()无法处理json字符串中的true,false,null。(因为python中没有null,且不支持首字母小写的true和false)
报错:NameError: name 'false' is not defined
粗暴的解决方法是定义全局变量:
class="highlighter-hljs hljs language-python hljs-line-numbers">global true, false, null true = false = null = ''
不过显然这种方法不够好,完全可以用字符串的replace方法或者re.sub正则来解决:
jsondata = re.sub('true', 'True', jsondata)
jsondata = re.sub('false', 'False', jsondata)
jsondata = re.sub('null', 'None', jsondata)
但还是不推荐使用eval来将字符串转为字典,因为eval的效率远低于json.dumps。
【参考:Python关于eval与json在字典转换方面的性能比较_janet1100的博客-CSDN博客】
综上,问题很睿智,以后要认真阅读Traceback,而不是直接复制粘贴搜索。
标签:TypeError,obj,Python,Object,jsondata,json,hljs,key,字典 From: https://www.cnblogs.com/victorique-de-blois/p/16997018.html