Skip to content

数据格式编码

更新: 2025/2/24 字数: 0 字 时长: 0 分钟

在爬取数据的过程或结果当中,我们会遇到各种各样的经过编码后的数据,熟知这些编码能更好规避采集过程中出现的错误。

JSON数据格式

JSON格式

**JSON 格式 ,即(JavaScript Object Notation)JavaScript 对象标记,它是网络响应中最常见的数据格式之一,也是大多数网站和数据接口服务所使用的格式。**特点如下:

  1. JSON 格式有两种,对象格式:{"key1":obj,"key2":obj...},数组/集合格式:[obj,obj,obj...]
  2. JSON 格式中的字符串没有单引号,全为双引号;
  3. 支持的数据类型:字符串、数字、布尔值、列表、字典。

**Python 中有内置的 json 模块专门处理 JSON 格式数据,直接导入就可以使用。**方法的使用案例如下:

python
# 导入json模块
import json

# json格式数据
data = '["iplaypython", [1, 2, 3], {"name": "xiaoming"}]'
# 将json格式数据转码成Python数据类型的数据
data = json.loads(data)
print(data, type(data))
# 将Python数据类型的数据编码为json格式的数据(indent参数表示可视化json的缩进空格数;ensure_ascii参数默认为True,会将汉字转为Unicode编码,可以指定False不转码汉字)
data = json.dumps(data)
print(data, type(data))
'''
输出:['iplaypython', [1, 2, 3], {'name': 'xiaoming'}] <class 'list'>
注释:json格式的数据里面的双引号全部变成了单引号,类型也从字符串变为了列表。
输出:["iplaypython", [1, 2, 3], {"name": "xiaoming"}] <class 'str'>
注释:将一个list列表对象进行json格式的转码,单引号变双引号,类型也从列表变为了JSON格式的字符串。
'''

**需要注意的是,并不是所有的字符串都可以进行 JSON 序列化,所以可以使用 json.loads 函数尝试将字符串解析为 JSON 对象,从而判断字符串是否可以进行 JSON 序列化。如果字符串是合法的 JSON 格式,json.loads 不会抛出异常,反之则会抛出异常。**以下是一个示例:

python
import json

def is_json_serializable(s):
    try:
        json.loads(s)
        return True
    except json.JSONDecodeError:
        return False

# 测试字符串
json_serializable_string = '{"key": "value"}'
non_json_serializable_string = 'not a valid json string'

# 判断字符串是否可JSON序列化
print(is_json_serializable(json_serializable_string))  # 输出 True
print(is_json_serializable(non_json_serializable_string))  # 输出 False

JSON文件

**存储 JSON 格式数据且后缀名为 .json 的文本文件就是 JSON 文件,上面介绍的 json 模块也有专门处理 JSON 文件的方法。**方法的使用案例如下:

python
# 导入json模块
import json

# 将文件对象和操作类型赋值给f(r代表读取)
with open('./tt.json', 'r') as f:
    # 将文件对象中的内容转码成python数据类型
    file_data = json.load(f)
    print(file_data, type(file_data))
'''
输出:[{"a": "aaa", "b": "bbb", "c": [1, 2, 3, [4, 5, 6]]}, 33, "tantengvip", true] <class 'list'>
'''

# 将文件对象和操作类型赋值给f(w代表写入)
with open('./ts.json', 'w') as f:
    # 将python数据类型转化为json格式数据
    json_data = json.dumps(file_data)
    # 将json格式的数据写入文件对象f中
    json.dump(json_data, f)
'''
新生成ts.json文件的内容为:"[{\"a\": \"aaa\", \"b\": \"bbb\", \"c\": [1, 2, 3, [4, 5, 6]]}, 33, \"tantengvip\", true]"
'''

字节编解码

字符串可以使用八进制字节码、十六进制字节码、Unicode(万国码)字节码来表示。

字节编码

python
string = 'abc'
o_s = ''.join([f'\\{ord(c):o}' for c in string])
print(o_s)  # 输出:\141\142\143。注释:八进制编码。
h_s = ''.join([f'\\x{ord(c):x}' for c in string])
print(h_s)  # 输出:\x61\x62\x63。注释:十六进制编码。
u_s = ''.join([f'\\u{ord(c):04x}' for c in string])
print(u_s)  # 输出:\u0061\u0062\u0063。注释:Unicode编码。

字节解码

字节解码不需要使用方法,Python 会自动识别其编码,为其解码:

python
print('\141\142\143')        # 输出:abc。注释:八进制写法。
print('\x61\x62\x63')        # 输出:abc。注释:十六进制写法。
print('\u0061\u0062\u0063')  # 输出:abc。注释:Unicode(万国码)表示法。
# 汉字编码范围十六进制(0x4e00, 0x9fa5)
for i in range(0x4e00, 0x9fa6):
    print(chr(i), end='')

URL编解码

URL的编码格式采用的是ASCII编码,所有的非ASCII字符都要编码之后再传输。

URL编码

**URL编码(URL encoding),也称作百分号编码(Percent-encoding), 是特定上下文的统一资源定位符 (URL)的编码机制。**其编码原理为,将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。比如,% 的ASCII码是37,对应16进制是25,那么 %% 的URL编码结果是 %25%25利用 urllib.parse 工具模块中的 quote 方法可以将内容转化为URL编码的格式。当URL中带有中文参数时,有可能导致乱码问题,此时用 quote 方法可以将中文字符转化为URL编码,适用于统一资源标识符(URI)的编码。

python
from urllib.parse import quote

url = 'https://www.baidu.com/s?wd=' + quote('公司')
print(url)  # 输出:https://www.baidu.com/s?wd=%E5%85%AC%E5%8F%B8
url = 'https://www.baidu.com/s?wd=' + quote(quote('公司'))
print(url)  # 输出:https://www.baidu.com/s?wd=%25E5%2585%25AC%25E5%258F%25B8

URL解码

**有了 quote 编码方法,当然就有 unquote 解码方法,它可以进行URL解码。**实例如下:

python
from urllib.parse import unquote

print(unquote('https://www.baidu.com/s?wd=%E5%85%AC%E5%8F%B8'))  # 输出:https://www.baidu.com/s?wd=公司
print(unquote(unquote('https://www.baidu.com/s?wd=%25E5%2585%25AC%25E5%258F%25B8')))  # 输出:https://www.baidu.com/s?wd=公司

有的请求中会将中文字符先进行一次 gbk编码,再进行URL编码,如果直接URL解码,会导致乱码,因为默认解码用的 utf-8编码,但我们可以通过encoding参数指定编码解决。

python
from urllib.parse import quote, unquote

# 先将中文字符'公司'编码成GBK编码,再编码为URL编码
result = quote('公司', encoding='gbk')
# 输出编码结果
print(result)                           # 输出:%B9%AB%CB%BE
# 以默认的utf-8编码方式进行URL解码
print(unquote(result))                  # 输出:��˾(乱码)
# 以GBK编码方式进行URL解码
print(unquote(result, encoding='gbk'))  # 输出:公司

URL序列化

**这里再介绍一个非常常用的 urlencode 方法,它在构造 GET 请求参数的时候非常有用,只需提前声明一个字典 params 将参数表示出来,然后调用 urlencode 方法就会自动的将 params 序列化为 GET 请求的参数,而且也可以将中文字符指定编码再转化为 URL 编码。**代码如下:

python
from urllib.parse import urlencode

params = {
    'name': 'germey',
    'age': 25,
    'wd': '公司'
}
# 默认的utf-8编码方式进行URL编码
url = 'https://www.baidu.com/s?' + urlencode(params)
print(url)  # 输出:https://www.baidu.com/s?name=germey&age=25&wd=%E5%85%AC%E5%8F%B8
# gbk编码方式进行URL编码
url = 'https://www.baidu.com/s?' + urlencode(params, encoding='gbk')
print(url)  # 输出:https://www.baidu.com/s?name=germey&age=25&wd=%B9%AB%CB%BE

URL反序列化

**有了序列化,必然会有反序列化,例用 parse_qsparse_qsl 方法,可以将一串GET请求参数返回序列化回字典或带元组的列表,也可以指定编码进行URL解码。**代码如下:

python
from urllib.parse import parse_qs, parse_qsl

query = 'name=germey&age=25&wd=%E5%85%AC%E5%8F%B8'
print(parse_qs(query))                   # 输出:{'name': ['germey'], 'age': ['25'], 'wd': ['公司']}
print(parse_qsl(query))                  # 输出:[('name', 'germey'), ('age', '25'), ('wd', '公司')]
query = 'name=germey&age=25&wd=%B9%AB%CB%BE'
print(parse_qs(query, encoding='gbk'))   # 输出:{'name': ['germey'], 'age': ['25'], 'wd': ['公司']}
print(parse_qsl(query, encoding='gbk'))  # 输出:[('name', 'germey'), ('age', '25'), ('wd', '公司')]

HTML编解码

HTML实体

**在 HTML 中一些字符是预留的,拥有特殊的含义,比如小于号 < 用于定义HTML标签的开始,所以这些字符是不允许在文本中使用的。**要想在 HTML 中显示 < 这个字符,就必须在 HTML 源码中插入字符实体,因此我们需要这样写 &lt; 或这样写 &#60;,可以看到实体由三部分组成:一个和号 & 和一个实体名称 lt 或者一个 #实体编号,以及一个分号 ;,对于中文来说,HTML实体就是 &#中文Unicode码;。建议在使用 HTML 实体时,用实体名称而不是实体编号,因为名称相对来说更容易记忆,几乎所有的浏览器对实体编号的支持都很好。

html模块

在 Python 中就有处理 HTML 实体的内置的 html 模块。使用如下:

python
# 导入内置的html模块
import html

# 字符串转HTML实体
str1 = '<随便写点代码>'
print(html.escape(str1))
# 将中文编码为html实体
str2 = ''.join([f'&#{ord(char)};' for char in str1])
print(str2)
# HTML实体转字符串
print(html.unescape(str2))
'''
输出:&lt;随便写点代码&gt;
注释:html模块方法不会将中文编码为HTML实体。
输出:&#60;&#38543;&#20415;&#20889;&#28857;&#20195;&#30721;&#62;
注释:&#60;对应上面的&lt;,&#62;对应上面的&gt;。
输出:<随便写点代码>
注释:将HTML实体转换为了符号和中文。
'''

警告

HTML实体对大小写敏感。

Base编解码

Base64编码

**Base64 是网络上最常见的用于传输字节码(Bit)的编码方式之一,是一种基于 64 个可打印字符(包括 A-Za-z0-9 一共是 62 个字符,另外两个可打印符号通常是 +/,而 = 用在最后进行补位)来表示二进制数据到字符的过程,可用于在 HTTP 环境下传递较长的标识信息或者编码各类文件。**例如在网络传输图片请求中,有的 Base64 编码以 data:image/jpg;base64 开头,表明这是 jpg格式图片 的 Base64 编码,而有的图片 Base64 编码就没有这个开头,只是一串 Base64 编码。

Base32编码

Base32和Base64大同小异,其最大区别在于:Base64中包含大写字母(A-Z)、小写字母(a-z)、数字0——9以及 +/;Base32中只有大写字母(A-Z)和数字234567。

base64模块

在 Python 中就有处理 Base64 编码、Base32 编码的内置的 base64 模块。使用如下:

python
# 导入base64模块
import base64

'''
字符编解码
'''
# 将字符串数据编码为字符类型的Base32编码
print(base64.b32encode('abc'.encode()).decode())       # 输出:'MFRGG==='
# 将字符类型的Base32编码解码为字符串
print(base64.b32decode('MFRGG==='.encode()).decode())  # 输出:'abc'
# 将字符串数据编码为字符类型的Base64编码
print(base64.b64encode('abc'.encode()).decode())       # 输出:'YWJj'
# 将字符类型的Base64编码解码为字符串
print(base64.b64decode('YWJj'.encode()).decode())      # 输出:'abc'

'''
文件编解码
'''
# 以字节模式读写文件
with open('old.png', 'rb') as f1:
    with open('new.png', 'wb') as f2:
        # 读取old.png文件的bytes类型的数据,将其编码为bytes类型的Base64编码,最后解码为字符类型的Base64编码
        img_base = base64.b64encode(f1.read()).decode()
        print(img_base)  # 输出:iVBORw0KGgoAAAA...(old.png文件的字符类型的Base64编码)
        # 将字符类型的Base64编码编码为bytes类型的Base64编码,最后解码为bytes类型的数据,写入新的new.png文件
        f2.write(base64.b64decode(img_base.encode()))

提醒

base64模块的编解码方法中传入的参数都是字节类型数据。

这个base64.b64decode('Tk8gUVE')为什么报错{Error}Incorrect padding?

报错 Incorrect padding 通常发生在 Base64 编码的字符串长度不符合正确的格式要求。Base64 编码要求输入的字符串长度必须是 4 的倍数,如果不是,必须在结尾添加 = 作为填充字符,以保证符合 Base64 的标准长度。

在你的例子中,Tk8gUVE 的长度是 7,不是 4 的倍数,缺少了必要的填充字符。

解决方法:

你可以手动添加 = 号来补足缺失的填充位。例如:

python
import base64

decoded = base64.b64decode('Tk8gUVE=')
print(decoded)

如果填充位缺失或不确定,可以通过添加 = 至长度为 4 的倍数来确保解码成功。

自动处理填充:

如果需要更通用的解决方案,自动补全 =,你可以这样做:

python
import base64

def fix_base64_padding(s):
    return s + '=' * (-len(s) % 4)

decoded = base64.b64decode(fix_base64_padding('Tk8gUVE'))
print(decoded)

这样可以保证自动填充,使得解码过程更灵活。