Python3 Json 汉字/转义字符编码问题
问题描述:
目标:在使用Beautiful Soup 4 抓取网页中文简体内容后以JSON格式输出到项目根目录
问题1: 输出结果中中文字符以编码形式呈现, e.g.: “当我 ”---- \u5f53\u6211
问题2: 换行,ampersand 符号也是以编码形式呈现, e.g.: 换行 ---- \n
问题3: 用json.loads() 读取文件时程序报错: json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
*
问题1 解决方案和过程
-
解决方案
- 使用 json.dump() 的 ensure_ascii=False 参数和 open() 的 encoding 参数
-
file = open(filename, ("w+","a+")[append],encoding="utf8") json.dump(self.__dict__, file, indent=4,ensure_ascii=False)
解决过程
首先我在输出JSON文件之前尝试了输出文本文件。用fileObject 的write() method输出是成功的,文本里的中文和符号都可以正常显示。然后尝试用json.dump()输出json文件,发现英文都能正常显示,但是问题1和问题2同时出现。
用postman确定了网页的content-type是text/UTF-8, 然后搜索了一下python str 的编码机制,发现在python3中所有字符都会被转译成Unicode处理,加上之前读写的成功,觉得问题是在json.dump()这里。 查阅了dump()的文档发现:
- “If ensure_ascii is true (the default), the output is guaranteed to have all incoming non-ASCII characters escaped. If ensure_ascii is false, these characters will be output as-is.”
所以之前输出时,非ascii字符依旧是以编码形式出现的。更改数值后,输出的编码格式应该是str 的默认编码Unicode。因为读取的文档是UTF-8 编码的,所以为了保持编码统一,应该在输出的file object 后面加上encoding="utf8"的参数。
结果发现汉字字符可以正常显示,但是换行和其他符号没有正常显示。
问题2 解决方案和过程
-
解决方案
- 没有需要解决的问题。 困惑来自于我自己没有理解json string的概念还有json和文本文档的不同。构建json.dump()使用的File object 时应该把读写行为设定为"a+"(默认行为)
解决过程
第二个问题来自于网页文本中“&”符号和后期format文字时添加的换行符号。无论 ensure_ascii() 和 econding 是否正确,这两个符号都会以编码的形式呈现。 ‘\n’ 在输出的时候被当作str的一部分了, 所以我怀疑转义字符在dump()的环节被再次转义了。dump看到一个需要转义的字符,于是就在他的前面添加了转义字符。为了测试这个猜想,我用json.load读取了该文件,发现果然每个’\'都被转义了。
我尝试在换行字符后加入‘\’,但是编译器报错,因为str 换行字符后面不能再添加转义字符。用 ‘\n’ 代替 ‘\n’ 也没有作用,load后发现两个转义字符都被转义了。用纯英文尝试一下是不是字符编码的问题,结果发现转义是dump()本身的表现。
dump()会把str 编码后以JSON的形式输出,而且在JSON spec中明确有提出JSON string是被双引号所包裹的,除了双引号、转义字符、和Control Character之外的任意Unicode编码字符组成的。换行在JSON当中属于Control Character, 所以dump()会自动把str当中包含的JSON Control Character(比如\n)转义,所以在生成的JSON String 当中,换行字符是以两个独立字符的形式出现的,这样保证读取的时候换行字符能够成功的转换回其本意。stdout输出时操作系统会根据换行符号展现出正确的格式,和json文件无关。
Stackoverflow上很棒的一个参考
问题3 解决方案和过程
-
解决方案
- 正确使用load() 和loads()
解决过程
load() 的参数是一个file descriptor, 可以设置编码等等,dump()和load()相似,输出对象也是文件。 loads()的参数是str,dumps()和loads()相同,得到的返回值是str。结合问题2,在使用load() 然后查询正确的key就可以输出格式正确的文本。
总结与收获
听说Python2的编码是天坑,Python3的编码好很多,但是对于我这个python+编程新手来说还是挺有挑战性的。粗略的了解了一下编码的理论,简单的学习了些python I/O, JSON 概念。还是有一些似懂非懂的地方,接下来继续学习,实践中理解,等有了更多的收获再回来进行修改和添加吧