这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战
你好,我是悦创。
最近在想如何把爬虫基础,联合起来形成大规模的爬虫实战,在实战的时候用到了 pickle 这就引出了这篇文章。
不正经定义
有些人对于专业晦涩难懂的定义,老是无法专心阅读。接下来我就先以一小段简短的对话,让你来快速了解 pickle 与 json 库的区别。
有个同学,小米过来问悦创,说:json 这个是干什么用的,和 pickle 有什么区别? 我的回答我觉得已经很通俗易懂了:
嗯, 我试着这么说: Python 这个人呢, 如果出远门的话是需要坐高铁的(各种数据库), 后来他买了辆小破车(pickle)做短途交通用。
就在近几年,打车软件火爆了,满街跑的都是**“滴滴”**(json),于是它下了一个软件(json 模块),需要时也用下滴滴(json), 后来就越来越多的人在用滴滴(json)了。
不知道,我这个答复,你觉得怎么样呢?
我就不说非常专业的术语来,但是你应该已经知道了,这两个库的小小关联了。不过就单单这么讲,肯定是不行的,接下来就正式的来讲一下吧。
Json 库
我来讲一个和实际应用很贴近的知识点。
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它的设计意图是把所有事情都用设计的字符串来表示,这样既方便在互联网上传递信息,也方便人进行阅读(相比一些 binary 的协议)。JSON 在当今互联网中应用非常广泛,也是每一个用 Python 程序员应当熟练掌握的技能点。
设想一个情景,你要向交易所购买一定数额的股票。那么,你需要提交股票代码、方向(买入 / 卖出)、订单类型(市价 / 限价)、价格(如果是限价单)、数量等一系列参数,而这些数据里,有字符串,有整数,有浮点数,甚至还有布尔型变量,全部混在一起并不方便交易所解包。
那该怎么办呢?
其实,我们要讲的 JSON ,正能解决这个场景。你可以把它简单地理解为两种黑箱:
- 第一种,输入这些杂七杂八的信息,比如 Python 字典,输出一个字符串;
- 第二种,输入这个字符串,可以输出包含原始信息的 Python 字典。
具体代码如下:
import json
params = {
'symbol': '123456',
'type': 'limit',
'price': 123.4,
'amount': 23
}
params_str = json.dumps(params)
print('after json serialization')
print('type of params_str = {}, params_str = {}'.format(type(params_str), params))
original_params = json.loads(params_str)
print('after json deserialization')
print('type of original_params = {}, original_params = {}'.format(type(original_params), original_params))
########## 输出 ##########
after json serialization
type of params_str = <class 'str'>, params_str = {'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}
after json deserialization
type of original_params = <class 'dict'>, original_params = {'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}
代码内操作:dumps、loads
是不是很简单呢?
不过还是那句话,请记得加上错误处理。不然,哪怕只是给 json.loads() 发送了一个非法字符串,而你没有 catch 到,程序就会崩溃了。「举个例子:类似,你的用户在注册的时候,传入了一个非法的字符串(可以理解为:不符合你设定的规则或者程序无法识别的(颜表情)等)这个时候你的程序就直接奔溃停止运行么?显然是不行的)
到这一步,你可能会想,如果我要输出字符串到文件,或者从文件中读取 JSON 字符串,又该怎么办呢?
是的,你仍然可以使用上面提到的 open() 和 read()/write() ,先将字符串读取 / 输出到内存,再进行 JSON 编码 / 解码,当然这有点麻烦。
import json
params = {
'symbol': '123456',
'type': 'limit',
'price': 123.4,
'amount': 23
}
with open('params.json', 'w') as fout:
params_str = json.dump(params, fout)
with open('params.json', 'r') as fin:
original_params = json.load(fin)
print('after json deserialization')
print('type of original_params = {}, original_params = {}'.format(type(original_params), original_params))
########## 输出 ##########
after json deserialization
type of original_params = <class 'dict'>, original_params = {'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}
文件流操作:dump、load
这样,我们就简单清晰地实现了读写 JSON 字符串的过程。当开发一个第三方应用程序时,你可以通过 JSON 将用户的个人配置输出到文件,方便下次程序启动时自动读取。这也是现在普遍运用的成熟做法。
那么 JSON 是唯一的选择吗? 显然不是,它只是轻量级应用中最方便的选择之一。据我所知,在 Google,有类似的工具叫做 Protocol Buffer,当然,Google 已经完全开源了这个工具,你可以自己了解一下使用方法。
相比于 JSON,它的优点是生成优化后的二进制文件,因此性能更好。但与此同时,生成的二进制序列,是不能直接阅读的。它在 TensorFlow 等很多对性能有要求的系统中都有广泛的应用。
说完 Json 之后,我们就要来说说 pickle 了。
Pickle 库
接下来,会讲到序列化和反序列化,所以我先分享一下这两个的定义。模块 Pickle 实现了对一个 Python 对象结构的二进制的序列化和反序列化。
- 我们把变量从内存中变成可存储或传输的过程称之为序列化,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
- 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即 unpickling。
即当 Python 程序持续运行一些字符串、列表字典、甚至自定义的类等数据对象,需要 持久化存储,即存储在磁盘中,防止运行在内存中,因断电等情况丢失数据。
那么 Pickle 模块就派上用场了,**它可以将对象转换为一种可以传输或存储的格式。**Python 的 pickle 模块实现了基本的数据序列和反序列化。通过 pickle 模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储;通过 pickle 模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。
与 JSON 模块的比较
可以看到 pickle 模块和看似相似,但其还是有着本质的不同,即:
- JSON 是一个文本序列化格式(它输出 unicode 文本,尽管在大多数时候它会接着以 utf-8 编码),而 pickle 是一个二进制序列化格式;
- JSON 是字面量可读的,而 pickle 不是(可以类比 base64 的不可读性);
- JSON 是可互操作的,在 Python 系统之外广泛使用,而 pickle 则是 Python 专用的;
序列化与反序列化
通过二进制的方式读写目标存储文件,并利用 dump 序列化数据对象,load 反序列化数据对象:
import pickle
D = {
'name': 'bob',
'major': {
'english',
'math'
},
'd': [1, 2, 3, 4, 5, 6, 7]
}
with open('D.pik', 'wb') as f:
pickle.dump(D, f)
with open('D.pik', 'rb') as f:
D = pickle.load(f)
print(type(D))
print(D)
示例结果:
<class 'dict'>
{'name': 'bob', 'major': {'english', 'math'}, 'd': [1, 2, 3, 4, 5, 6, 7]}
当然我们也可以序列化到内存(字符串格式保存),然后对象可以以任何方式处理如通过网络传输:
pik = pickle.dumps(D)
print(pik)
D = pickle.loads(pik)
print(type(D))
print(D)
示例结果:
b'\x80\x04\x95E\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x03bob\x94\x8c\x05major\x94\x8f\x94(\x8c\x07english\x94\x8c\x04math\x94\x90\x8c\x01d\x94]\x94(K\x01K\x02K\x03K\x04K\x05K\x06K\x07eu.'
<class 'dict'>
{'name': 'bob', 'major': {'english', 'math'}, 'd': [1, 2, 3, 4, 5, 6, 7]}
cPickle
cPickle 包的功能和用法与 pickle 包几乎完全相同 (其存在差别的地方实际上很少用到),不同在于 cPickle 是基于 c 语言编写的,具有更好的性能,对于大多数应用程序,推荐使用该模块。对于上面的例子,如果想使用 cPickle 包,我们都可以将 import 语句改为 import cPickle as pickle 进行使用。
AI悦创:V:Jiabcdefh