Python Beautiful Soup gb2312 Windows-1252 乱码问题

当初,Python 的编码问题把我折磨得死去活来,一直准备写一篇文章总结一下。这里先把当时对我最有用的两篇文章和关键结论整理出来,方便以后遇到 Beautiful Soup、gb2312gbkgb18030Windows-1252 乱码时快速定位。

问题现象

抓取中文网页或 RSS 时,页面声明自己是 gb2312gbk,但解析出来的中文却变成乱码。有时 Beautiful Soup、feedparser 或其他库会把编码识别成 windows-1252,于是中文内容完全不可读。

常见原因不是解析库完全不认识 gb2312,而是网页实际内容并不严格属于 gb2312 字符集。很多中文网页会把 gb2312gbk 混用,甚至声明为 gb2312,但正文里已经包含了超出 gb2312 范围的字符。严格检测时,一旦发现字节不符合 gb2312,解析器可能回退到 windows-1252,结果就出现乱码。

关键结论

请注意:很多网页标称的 gb2312 不一定真是严格的 gb2312。遇到这类页面时,可以优先尝试用 gb18030 解码。

gb18030 向下兼容 gbkgb2312,覆盖范围更大。微软也常把 gb2312gbk 映射到更宽的中文编码处理方式,这方便了一些场景,也容易让编码声明和真实内容混在一起。

也就是说,页面虽然可能写着:

<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。这样中文字节就会被按西文编码解释,最终显示为乱码。

所以问题不一定是“库太笨”,而是“网页声明的编码和真实字节不一致”。

排查步骤

遇到中文网页乱码时,可以按这个顺序检查:

  1. 保留原始字节,不要过早 .decode()
  2. 查看 HTTP 头里的 Content-Type 和 HTML 里的 meta charset
  3. 如果声明为 gb2312gbk,但解析失败,尝试 gb18030
  4. 打印 repr() 或写入文件检查,不要只看终端显示结果。
  5. 确认终端、编辑器、数据库连接和输出文件本身是否使用 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 乱码问题

文章 2:

小结

处理中文网页乱码时,最重要的是区分“声明编码”和“真实编码”。很多老网页写着 gb2312,实际却更接近 gbkgb18030。对 Beautiful Soup、feedparser 这类解析工具来说,直接指定 gb18030 往往比相信网页声明更可靠。

Leave a Reply