乱码本质是字节流解码时编码不匹配,应优先用response.content手动解码,按响应头→html meta→charset_normalizer顺序确定编码,避免依赖response.text。

直接结论:乱码不是网页有问题,而是 Python 把字节流错误映射成了字符串——关键在 response.content 到 str 这一步没对上编码。
requests.text 为什么一用就乱?
因为 response.text 是 requests 自动调用 response.content.decode(response.encoding) 的结果,而 response.encoding 的推导顺序是:Content-Type 响应头 > <meta> 标签 > chardet。任意一环出错(比如老站声明 charset=utf-8 实际发的是 gbk),response.text 就不可信,且改 response.encoding 后再读 .text 不会重解码。
- 永远优先用
response.content(原始bytes),别依赖.text - 如果非要用
.text,必须在首次访问前设好response.encoding,设晚了无效 -
response.apparent_encoding是 requests 内置的chardet粗略检测,不准时别硬信
怎么安全地从 content 解码出正确字符串?
手动解码才是可控路径,但不能瞎试。得按可信度降序取编码源:
- 先查响应头:
response.headers.get('content-type', ''),正则提取charset=([^;]+) - 再查 HTML meta:
re.search(rb'<meta>]+charset=[\'"]?([^\'">]+)', response.content),只搜前 10KB 避免慢 - 最后 fallback 到
charset_normalizer(比chardet更准更快,尤其对中文):from charset_normalizer import from_bytes; r = from_bytes(response.content); encoding = r[0].confidence > 0.7 and r[0].encoding or 'gb18030' - 解码时务必加
errors='replace'或errors='ignore',防止个别坏字节崩掉整个解析
保存 CSV / TXT 文件还是乱码?
写文件乱码和爬取乱码是两件事:前者是 Python 写入时用了错误编码,后者是读取时解错了。常见坑:
- 用
open(..., 'w')必须显式传encoding='utf-8',Python 默认编码不一定是 UTF-8 - 存 CSV 给 Excel 看,一定要用
encoding='utf-8-sig',否则首列多出\ufeff - 如果原始内容是
gbk编码,又想存成 UTF-8 文件,别用str.encode().decode()转换,直接:text_gbk = response.content.decode('gbk'); text_utf8 = text_gbk.encode('utf-8').decode('utf-8') - 用
csv.writer时,newline=''必须带上,否则 Windows 下会多空行
为什么试了 utf-8 还是乱,换成 gbk 就正常?
这不是玄学,是现实:大量中文站点用 gbk 或 gb18030 编码,但响应头或 meta 标签写的是 utf-8(甚至不写)。这种「声明与实际不符」的情况非常普遍。
- 遇到乱码,第一反应不该是换库,而是试
gb18030(GBK 超集,兼容性最好):response.content.decode('gb18030', errors='ignore') - 如果页面含繁体、生僻字,
gb18030比gbk更稳妥 - 不要迷信
chardet对短文本或压缩响应的判断,它对纯 ASCII 占比高的 UTF-8 页面常误判为ascii - 真正难搞的是混合编码页面(比如 JS 插入的局部内容用不同编码),这时得局部提取 + 单独 decode
最易被忽略的点:乱码问题本质是字节与字符的映射断裂,而这个映射信息分散在 HTTP 头、HTML meta、字节本身三个地方——你得把它们都检查一遍,而不是只盯一个环节。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











