杰瑞科技汇

Python pymysql编码问题如何解决?

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

Python pymysql编码问题如何解决?-图1
(图片来源网络,侵删)

下面我将从核心问题、解决方案、最佳实践和常见问题四个方面进行阐述。


核心问题:编码不匹配

编码问题通常发生在以下几个环节:

  1. 数据库层面:你的 MySQL 数据库、表、列的默认字符集是什么?如果设置为 latin1,它就无法正确存储和检索中文字符。
  2. Python 代码层面:Python 3 的字符串默认是 Unicode (UTF-8),你的 Python 脚本源文件是否也保存为了 UTF-8 编码?
  3. 连接层面pymysql 在连接数据库时,可以指定一个“连接字符集”,如果这个字符集与数据库或表的不匹配,就会导致数据在传输过程中被错误地转换。

目标:确保这三个层面的字符集统一utf8mb4 (强烈推荐) 或 utf8


解决方案与最佳实践

数据库层面的设置(最重要)

这是解决问题的基石,如果你的数据库字符集设置不当,任何应用层的技巧都只是“治标不治本”。

Python pymysql编码问题如何解决?-图2
(图片来源网络,侵删)

检查和设置字符集:

你可以通过以下 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 pymysql编码问题如何解决?-图3
(图片来源网络,侵删)

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 编码)。
  • 解决方案
    1. 检查你的数据库、表、列的字符集,确保它们是 utf8mb4
    2. 检查你的 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

要彻底解决 pymysql 的编码问题,请遵循以下黄金法则:

  1. 数据库层面统一使用 utf8mb4 作为数据库、表、列的默认字符集。
  2. 连接层面:在 pymysql.connect() 中,务必设置 charset='utf8mb4'
  3. 代码层面:确保你的 .py 文件是 UTF-8 编码保存的。

只要这三者保持一致,你就可以在 Python 和 MySQL 之间无缝地传输任何文本数据,包括中文和 Emoji。

分享:
扫描分享到社交APP
上一篇
下一篇