踩坑纪实:UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 21-22: invalid continuation byte
问题出现过程
切换新的服务器之后,使用PyExecJS库报错
...
ctx = execjs.compile(js_str)
version_obj = ctx.eval('exportObj')
报错内容:
UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 21-22: invalid continuation byte
排查踩坑过程
根据报错大概可以猜测是编码解析异常,于是网上搜索+ChatGPT搜索查询原因,进行排查调试:
-
首先当然是“控制变量法”,原服务器是一切正常的,为什么切换新服务器就报错了呢?
但是检查系统环境和软件环境,安装的linux版本、python版本、依赖包的版本都与旧服务器完全一致,是不是有“玄学”?? -
项目里的使用方式是读取js文件再用execjs解析,怀疑是不是读取文件的方式有问题,于是将原先的
r
方式改为rb
再解码,没有任何效果; -
根据报错还不知道具体是那个字符解析出错,反复换了几个js文件之后仍然没有作用,于是排除变量,排除django项目、读取文件等因素的影响,直接在python命令行对字符串解析,最终发现还是报错,但是定位到原因是中文字符无法解析;
-
类似的中文字符解析问题之前有遇到过,在windows上会出现
UnicodeDecodeError: 'GBK' codec
错误,需要修改原生的subprocess.py文件解决,或者采用如下方式,于是进行尝试,但是无效;import subprocess from functools import partial # **需在 import execjs之前** 处理execjs编码报错问题,将 encoding 参数直接置为 UTF-8 编码格式 subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
ps: 上述代码忘了是在哪篇文章看到的了,比修改原生文件的方式好,记下来备用。
-
指定execjs的运行环境:
os.environ["EXECJS_RUNTIME"] = "JScript"
os.environ["EXECJS_RUNTIME"] = "Node"
加上相关代码后仍然无效(其实这个才是关键,后面会讲)
-
python3的编码问题,在py文件开头加上# -- coding: utf-8 --,或者尝试对字符串编码再解码等,都无效;
-
还有一系列绞尽脑汁的重装python,升级python版本等无效操作...
最终解决方案
尝试了无数方案后,复盘代码,发现可能还是运行execjs导致的问题,偶然间在一个网页上找到execjs.get().name
可以获取执行环境,于是再试试:
>>> import execjs
>>> execjs.get().name
'SpiderMonkey'
>>> import os
>>> os.environ["EXECJS_RUNTIME"] = "Node"
>>> execjs.get().name
'SpiderMonkey'
>>> os.environ["EXECJS_RUNTIME"] = "JScript"
>>> execjs.get().name
'SpiderMonkey'
发现没有?在执行os.environ["EXECJS_RUNTIME"] = "xxx"
前后运行环境不会有任何改变,这是什么原因呢?
再在旧服务器上执行execjs.get().name
,结果得到的环境是'Node.js (V8)'
噢,因为新服务器没有使用到node环境,所以没有安装nodejs!
于是在新服务器上安装同版本nodejs,执行成功,大功告成!