当初,Python 的编码问题把我折磨得死去活来,一直准备写一篇文章总结一下。这里先把当时对我最有用的两篇文章和关键结论整理出来,方便以后遇到 Beautiful Soup、gb2312、gbk、gb18030、Windows-1252 乱码时快速定位。
Table of Contents
问题现象
抓取中文网页或 RSS 时,页面声明自己是 gb2312 或 gbk,但解析出来的中文却变成乱码。有时 Beautiful Soup、feedparser 或其他库会把编码识别成 windows-1252,于是中文内容完全不可读。
常见原因不是解析库完全不认识 gb2312,而是网页实际内容并不严格属于 gb2312 字符集。很多中文网页会把 gb2312、gbk 混用,甚至声明为 gb2312,但正文里已经包含了超出 gb2312 范围的字符。严格检测时,一旦发现字节不符合 gb2312,解析器可能回退到 windows-1252,结果就出现乱码。
关键结论
请注意:很多网页标称的 gb2312 不一定真是严格的 gb2312。遇到这类页面时,可以优先尝试用 gb18030 解码。
gb18030 向下兼容 gbk 和 gb2312,覆盖范围更大。微软也常把 gb2312、gbk 映射到更宽的中文编码处理方式,这方便了一些场景,也容易让编码声明和真实内容混在一起。
也就是说,页面虽然可能写着:
<meta charset="gb2312">
但实际内容更可能需要按 gb18030 处理。
Beautiful Soup 的处理方法
如果使用 Python 2 时代的写法,可以在传给 Beautiful Soup 时指定编码:
page = urllib2.build_opener().open(req).read()
soup = BeautifulSoup(page, fromEncoding="GB18030")
这样做的重点是:不要完全相信 HTML 里声明的 gb2312,而是把原始字节按更宽的 GB18030 交给解析器。
如果是较新的 Beautiful Soup 版本,参数名通常写作 from_encoding:
from bs4 import BeautifulSoup
html = response.content
soup = BeautifulSoup(html, "html.parser", from_encoding="gb18030")
如果你已经手动解码,也可以先把字节转成 Unicode 字符串,再交给 Beautiful Soup:
text = response.content.decode("gb18030", errors="replace")
soup = BeautifulSoup(text, "html.parser")
errors="replace" 会把无法解码的字节替换掉,适合抓取时尽量保留正文。调试时也可以先不用它,让异常暴露出来,确认到底是哪一段字节有问题。
为什么会被识别成 Windows-1252
一个典型例子是百度新闻 RSS:页面声明编码为 gb2312,但实际内容中可能包含 gbk 范围内的字符。feedparser 严格按 gb2312 检查时,发现有字符超出 gb2312 范围,于是放弃 gb2312,回退成 windows-1252。这样中文字节就会被按西文编码解释,最终显示为乱码。
所以问题不一定是“库太笨”,而是“网页声明的编码和真实字节不一致”。
排查步骤
遇到中文网页乱码时,可以按这个顺序检查:
- 保留原始字节,不要过早
.decode()。 - 查看 HTTP 头里的
Content-Type和 HTML 里的meta charset。 - 如果声明为
gb2312或gbk,但解析失败,尝试gb18030。 - 打印
repr()或写入文件检查,不要只看终端显示结果。 - 确认终端、编辑器、数据库连接和输出文件本身是否使用 UTF-8。
示例:
raw = response.content
for encoding in ["utf-8", "gb18030", "gbk", "gb2312"]:
try:
text = raw.decode(encoding)
print("decoded as", encoding)
break
except UnicodeDecodeError:
pass
如果 gb2312 失败而 gb18030 成功,基本就能说明页面声明不够准确。
参考文章
文章 1:Beautiful Soup gb2312 乱码问题
- http://powerelite.blog.163.com/blog/static/429658912014394820777/
- http://groups.google.com/group/python-cn/browse_thread/thread/cb418ce811563524
- 上午解决了网页解析乱码的问题
- http://blog.csdn.net/fanfan19881119/article/details/6789366
- 原始出处:http://leeon.me/a/beautifulsoup-chinese-page-resolve
文章 2:
小结
处理中文网页乱码时,最重要的是区分“声明编码”和“真实编码”。很多老网页写着 gb2312,实际却更接近 gbk 或 gb18030。对 Beautiful Soup、feedparser 这类解析工具来说,直接指定 gb18030 往往比相信网页声明更可靠。
