数据库的字符集、Python 字符串的编码、以及连接的编码设置三者之间不一致。

下面我将从核心问题、解决方案、最佳实践和常见问题四个方面进行阐述。
核心问题:编码不匹配
编码问题通常发生在以下几个环节:
- 数据库层面:你的 MySQL 数据库、表、列的默认字符集是什么?如果设置为
latin1,它就无法正确存储和检索中文字符。 - Python 代码层面:Python 3 的字符串默认是 Unicode (UTF-8),你的 Python 脚本源文件是否也保存为了 UTF-8 编码?
- 连接层面:
pymysql在连接数据库时,可以指定一个“连接字符集”,如果这个字符集与数据库或表的不匹配,就会导致数据在传输过程中被错误地转换。
目标:确保这三个层面的字符集统一为 utf8mb4 (强烈推荐) 或 utf8。
解决方案与最佳实践
数据库层面的设置(最重要)
这是解决问题的基石,如果你的数据库字符集设置不当,任何应用层的技巧都只是“治标不治本”。

检查和设置字符集:
你可以通过以下 SQL 命令检查和修改。
-- 1. 查看数据库的字符集
SHOW VARIABLES LIKE 'character_set_database';
-- 2. 查看表的字符集
SHOW TABLE STATUS WHERE Name = 'your_table_name';
-- 3. 查看列的字符集
SHOW FULL COLUMNS FROM your_table_name;
-- 4. 创建数据库时指定字符集
CREATE DATABASE my_database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 5. 创建表时指定字符集
CREATE TABLE my_table (
id INT PRIMARY KEY,
content VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 6. 修改已存在的数据库字符集(谨慎操作)
ALTER DATABASE my_database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 7. 修改已存在的表字符集
ALTER TABLE my_table CONVERT TO CHARACTER SET utf8mb4;
为什么推荐 utf8mb4 而不是 utf8?
- MySQL 的
utf8:这是一个“伪” UTF-8,它最多只能支持 3 个字节的字符,它可以表示大部分的 Unicode 字符,但无法表示 Emoji 表情符号,以及一些不常用的汉字(如 "𠮷")。 - MySQL 的
utf8mb4:这是一个完整的、真正的 UTF-8 实现,它使用 1 到 4 个字节来表示字符,它可以存储任何 Unicode 字符,包括 Emoji,它是处理多语言和特殊符号时的最佳选择。
最佳实践:在创建新项目时,始终将数据库、表、列的默认字符集都设置为 utf8mb4。

Python 脚本层面的设置
确保你的 Python 源代码文件本身是 UTF-8 编码保存的。
- 在现代编辑器(如 VS Code, PyCharm)中,新建文件并保存为 UTF-8 是默认行为。
- 你可以在 Python 文件的开头加上一个“编码声明”,这是一种好习惯,尽管在 Python 3 中通常不是必需的。
# -*- coding: utf-8 -*- # 你的 Python 代码
PyMySQL 连接层面的设置(关键步骤)
这是在代码中直接解决编码问题的关键,在建立 pymysql 连接时,通过 charset 参数指定连接的字符集。
正确的方式:
import pymysql
# ... 其他连接参数 ...
connection = pymysql.connect(
host='localhost',
user='your_user',
password='your_password',
database='your_database',
charset='utf8mb4', # <--- 关键:指定连接字符集为 utf8mb4
cursorclass=pymysql.cursors.DictCursor
)
为什么这里也要设置 charset?
pymysql 在连接数据库后,会执行一个 SET NAMES <charset> 命令来告诉 MySQL:“接下来我们通过这个连接传输的数据,都使用 <charset> 编码”,如果这个设置与数据库的字符集不匹配,MySQL 就会尝试进行转换,如果转换失败(比如用 latin1 去解码一个 UTF-8 的字节流),就会出现乱码或 Incorrect string value 错误。
autocommit 参数
建议也一并设置 autocommit,这样你的 INSERT, UPDATE 操作会立即生效,不需要手动调用 connection.commit()。
connection = pymysql.connect(
# ... 其他参数 ...
charset='utf8mb4',
autocommit=True # <--- 自动提交事务
)
完整代码示例
下面是一个完整的、遵循了最佳实践的代码示例,包含中文、Emoji 和特殊字符的插入与查询。
import pymysql
import sys
# 确保你的终端/控制台支持 UTF-8 输出,否则打印时也可能乱码
# sys.stdout.reconfigure(encoding='utf-8')
# 1. 定义数据库连接配置
db_config = {
'host': 'localhost',
'user': 'root',
'password': 'your_password',
'database': 'test_db', # 假设这个数据库和表都已设置为 utf8mb4
'charset': 'utf8mb4', # <--- 关键:连接字符集
'cursorclass': pymysql.cursors.DictCursor,
'autocommit': True # <--- 自动提交
}
# 2. 测试数据(包含中文、Emoji和特殊字符)
test_data = {
'chinese_text': '你好,世界!',
'emoji_text': '这是一个笑脸 😊 和一个火箭 🚀。',
'special_char': '特殊字符:© ® ™'
}
try:
# 3. 建立数据库连接
print("正在连接数据库...")
connection = pymysql.connect(**db_config)
print("数据库连接成功!")
# 4. 获取游标
with connection.cursor() as cursor:
# 5. 插入数据
print("\n正在插入数据...")
sql_insert = "INSERT INTO my_table (content) VALUES (%s)"
# 使用 executemany 可以插入多行
cursor.executemany(sql_insert, [test_data['chinese_text'], test_data['emoji_text'], test_data['special_char']])
print("数据插入成功!")
# 6. 查询数据
print("\n正在查询数据...")
sql_select = "SELECT id, content FROM my_table"
cursor.execute(sql_select)
results = cursor.fetchall()
# 7. 打印查询结果
print("\n查询结果:")
for row in results:
# Python 3 的 print 默认能处理 Unicode,所以直接打印即可
print(f"ID: {row['id']}, Content: {row['content']}")
except pymysql.MySQLError as e:
print(f"数据库操作出错: {e}")
finally:
# 8. 关闭数据库连接
if 'connection' in locals() and connection.open:
connection.close()
print("\n数据库连接已关闭。")
常见问题与排查
错误:pymysql.err.ProgrammingError: (1366, "Incorrect string value: '\\xF0\\x9F\\x98\\x8A' for column 'content' at row 1")
- 原因:这是最典型的编码错误,意思是 MySQL 试图用
latin1(或其他不支持4字节字符的编码)去解码一个 4 字节的 UTF-8 序列(\\xF0...Emoji 的 UTF-8 编码)。 - 解决方案:
- 检查你的数据库、表、列的字符集,确保它们是
utf8mb4。 - 检查你的
pymysql.connect()中的charset参数,确保是utf8mb4。
- 检查你的数据库、表、列的字符集,确保它们是
错误:pymysql.err.OperationalError: (2025, "Cannot connect to MySQL server on '...'")
- 原因:有时字符集不匹配也可能导致连接失败,尤其是在某些旧版本的 MySQL 或特定配置下。
- 解决方案:确保连接参数正确,特别是
charset。
控制台打印出来是乱码,但数据库里存的是对的
- 原因:这不是
pymysql的问题,而是你的终端或 IDE 控制台的编码问题,它可能被设置为GBK或其他编码,无法正确显示 UTF-8 字符。 - 解决方案:
- Windows: 在终端属性中,将“代码页”更改为
65001 (UTF-8)。 - macOS / Linux: 通常默认就是 UTF-8,如果不是,可以尝试
export LANG=en_US.UTF-8。 - IDE (如 PyCharm): 在
Run/Debug Configurations中,确保VM options里没有-Dfile.encoding=GBK之类的设置,或者直接设置为-Dfile.encoding=UTF-8。
- Windows: 在终端属性中,将“代码页”更改为
要彻底解决 pymysql 的编码问题,请遵循以下黄金法则:
- 数据库层面:统一使用
utf8mb4作为数据库、表、列的默认字符集。 - 连接层面:在
pymysql.connect()中,务必设置charset='utf8mb4'。 - 代码层面:确保你的
.py文件是 UTF-8 编码保存的。
只要这三者保持一致,你就可以在 Python 和 MySQL 之间无缝地传输任何文本数据,包括中文和 Emoji。
