如何在Python中忽略UTF-8四字节字符?Windows环境爬取问题
问题背景
我最近在用Python做网页爬取,目标页面是UTF-8编码的,但碰到了四字节UTF-8字符Ƣ(对应字节串b'\xf0\xa0\x86\xa2')的处理难题。作为Windows用户,我清楚系统对UTF-8的支持一直不太友好,折腾了好几个小时想解析文本并移除这类异常字符,却始终没搞定。我的需求很明确:只移除那些无法正常显示的四字节字符,保留整行里的其他内容。
最开始我写了这样一个处理函数:
def TryDecode(toParse): try: result = toParse.decode('utf-8', 'ignore') # 这里没抛出异常 except UnicodeEncodeError: result = 'error' return result badutf = b' <li ...>\xf0\xa0\x86\xa2</li>\r\n' res = TryDecode(badutf) print("I see this") print(res) # 在这里触发UnicodeEncodeError print("I do not see this.")
预期与实际结果差异
- 预期:要么在try解码环节抛出错误,要么顺利完成解码无异常
- 实际:解码过程完全正常,但执行第二个
print语句时才突然触发UnicodeEncodeError,导致后续代码无法执行
另外还有个小坑:如果脚本里直接写了Ƣ这个字符,我的IDE甚至都没法正常运行脚本。
问题根源分析
其实你误解了错误发生的阶段——解码环节根本没出错!decode('utf-8', 'ignore')已经成功把字节串转换成了Unicode字符串,报错是因为后续的控制台输出编码问题:Windows控制台默认的编码(比如GBK或者UTF-16LE)不支持四字节的Unicode字符(也就是超出UCS-2/BMP范围的字符),当print尝试把Unicode字符串编码成控制台能识别的格式时,就抛出了UnicodeEncodeError。而你的try-except只捕获了解码时的异常,完全没覆盖到输出阶段的错误,所以才会出现代码执行到print才崩溃的情况。
可行解决方案
既然Windows控制台只支持UCS-2范围内的字符(即码点小于65536的字符),我们可以在解码后主动过滤掉那些超出这个范围的字符,只保留能正常显示的内容。修改后的函数如下:
UCSTWOMAX = 65536 # UCS-2支持的最大字符码点 def TryDecode(toParse): try: parsed = toParse.decode('utf-8', 'ignore') result = '' for c in parsed: if ord(c) < UCSTWOMAX: result += c except UnicodeEncodeError: result = 'error' return result badutf = b' <li ...>\xf0\xa0\x86\xa2</li>\r\n' res = TryDecode(badutf) print(res) print("I see this now.")
这个方案的核心逻辑是:先正常解码字节串,然后遍历每个字符,只保留码点小于65536的字符,这样输出到Windows控制台时就不会再触发编码错误,同时也完整保留了文本里的其他有效内容。
内容的提问来源于stack exchange,提问作者Spiros

