Python3.6 字符编码问题如何解决?

准备写个爬虫,监控一个网页,如果有更新就将更新的内容采集并邮件通知我,结果开始就卡住了。。。

问题:月份提取出来中文显示为乱码,如:2018å¹´05期

我看了网页源码,有声明 charset=utf-8, 并且我用的是 python3.6,所以比较纳闷为何为出现乱码,在 Chrome 控制台下测试 xpath 时是没毛病的:

然后各种百度、谷歌的找,大部说到是编码问题,一篇篇的关于编码的文章看得脑壳麻,然后按所说的方法都不能解决,特发贴看有遇到同样问题的朋友没

尝试过的方法:

  • sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8')
  • 编码转换, text.encode('utf-8').decode('unicode_escape')

PS: 打印 requests.get()text 所有中文都显示为乱码

下面为测试的 demo:

import requests
'''
    import re
    import sys
    import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8')

‘’’ from lxml import html

url = ‘http://www.wh-ccic.com.cn/node_13613.htm

headers = { ‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36’ } base_url = ‘http://www.wh-ccic.com.cn’ page = requests.get(url, headers=headers) tree = html.fromstring(page.text) print(page.text) all_a = tree.xpath(’.//*[@class=“STYLE13”]/a’) for a in all_a: # print(a.attrs[‘href’]) # href = a.attrs[‘href’] # title = a.text.replace(u’\xe5’, u’ ') href = a.attrib[‘href’] title = a.text if ‘\u’ in title: title = title.encode(‘utf-8’).decode(‘unicode_escape’) print(title)


Python3.6 字符编码问题如何解决?

12 回复

page = requests 后面加一行 page.encoding=‘utf-8’


在Python 3.6中处理字符编码问题,核心是理解str(Unicode字符串)和bytes(原始字节)的区别,并确保在正确的边界进行编解码。

关键点:

  1. 输入/读取数据时:将bytes解码(decode)为str
  2. 输出/写入数据时:将str编码(encode)为bytes
  3. 在代码内部:尽量只使用str类型进行处理。

常见场景与代码示例:

场景1:读取文件时指定编码

# 错误做法:二进制模式读取文本文件,得到bytes
with open('file.txt', 'rb') as f:
    data = f.read()  # data是bytes类型

# 正确做法:文本模式读取,指定编码
with open('file.txt', 'r', encoding='utf-8') as f:  # 根据文件实际编码调整
    text = f.read()  # text是str类型,可以直接处理

场景2:网络请求或数据库读取

import requests

resp = requests.get('http://example.com')
# resp.content 是 bytes
# resp.text 是 str(requests会自动解码)

# 如果自动解码失败,手动指定编码
resp.encoding = 'gbk'  # 或 'utf-8' 等
text = resp.text

场景3:处理来源不明的字节数据

# 假设收到一段字节数据
raw_bytes = b'\xe4\xb8\xad\xe6\x96\x87'

# 尝试常用编码解码
encodings = ['utf-8', 'gbk', 'latin-1', 'cp1252']
for enc in encodings:
    try:
        text = raw_bytes.decode(enc)
        print(f"成功解码 ({enc}): {text}")
        break
    except UnicodeDecodeError:
        continue

场景4:写入文件

text = "中文内容"
# 写入文本文件
with open('output.txt', 'w', encoding='utf-8') as f:
    f.write(text)  # 自动按指定编码转换

# 写入二进制文件
with open('output.bin', 'wb') as f:
    f.write(text.encode('utf-8'))  # 手动编码

通用建议:

  • 在代码开头统一声明编码:# -*- coding: utf-8 -*-
  • 尽量使用UTF-8编码
  • 避免混合使用strbytes类型操作
  • 使用chardet库检测未知数据的编码

一句话总结: 明确数据边界,在I/O环节做好编解码,内部统一用Unicode字符串处理。

楼上说的对

requests 是通过 response 的头部( headers )来检测编码的。
虽然这页面在源码里声明了 charset,但是没在 ‘Content-Type’ header 里声明 charset:
Content-Type: text/html

所以 requests 就使用了默认的编码 ‘ISO-8859-1’:
>>> page.encoding
’ISO-8859-1’

于是就出现乱码问题了。

解决方法是手动设置 requests 解码这个页面使用的编码:
>>> page.encoding = ‘utf-8’
>>> tree = html.fromstring(page.text)
>>> title = tree.xpath(’.//*[@class=“STYLE13”]/a’)[0].text
>>> title
’2018 年 05 期’

放上 requests 文档中关于编码的说明:

Encodings

When you receive a response, Requests makes a guess at the encoding to use for decoding the response when you access the Response.text attribute. Requests will first check for an encoding in the HTTP header, and if none is present, will use chardet to attempt to guess the encoding.

The only time Requests will not do this is if no explicit charset is present in the HTTP headers and the Content-Type header contains text. In this situation, RFC 2616 specifies that the default charset must be ISO-8859-1. Requests follows the specification in this case. If you require a different encoding, you can manually set the Response.encoding property, or use the raw Response.content.

http://docs.python-requests.org/en/latest/user/advanced/#encodings

也是遇到编码问题,至今没搞懂 2 和 3 的差别。只是觉得 2 更好用

1 楼正解

当然是 2 不好用…不过 lz 的问题主要是 requests 非得按照标准来,不处理非标准情况的一些 corner case (当然基本上都是半瓶水码农的锅)。 https://github.com/requests/requests/issues/1737

很多网站都有这个问题,一般国内默认 utf-8 就好。实在拿不准编码的时候也可以通过 page.content.decode($codec) 一个个试

因为获取回来的 text 中的中文已经是乱码,所以也怀疑是 requests 那里有问题,可是不知如何解决,又找不到详细的 api 说明,还尝试过在 get 方法里面加 encoding 参数,结果是没有那个参数,却没想到变通一下多写一句。。。

2 貌似读进来的都是编码格式的,而 3 貌似是默认 unicode,调用 chardet 就会出错。我的问题更奇葩,就是各种 utf-8,gb2312,gbk 混编,一行内各种编码混杂。。。。始终没有很完美的解决方法

不知道编码的时候试试
resp.encoding=resp.apparent_encoding

回到顶部