Pandas read_sql_table连PostgreSQL报UnicodeDecodeError
问题根因
PostgreSQL服务端编码为SQL_ASCII时,不会对存储的内容做任何编码合法性校验,也不会在返回数据时执行编码转换——无论客户端编码设置为什么值,服务端都会直接返回原始存储的字节流。你遇到的0xa0字节不属于合法UTF-8序列(该字节在CP1252/Latin1编码中为不间断空格,在GBK/GB18030编码中为常见中文字节),因此psycopg2驱动按UTF-8规则解码时必然抛出错误。
解决方案
方案1:连接层配置容错解码(推荐,适配非关键分析场景)
不需要修改上层pandas读取逻辑,直接在psycopg2连接参数中开启解码容错,遇到非法字节时自动替换为�,不会中断读取流程,完全满足你查找指定字符串、导出CSV的需求:
from sqlalchemy import create_engine import pandas conn_str = f"postgresql+psycopg2://{config['DBUSER']}:{config['DBPASS']}@{config['DBHOST']}/{config['DBNAME']}" engine = create_engine( conn_str, pool_recycle=36000, connect_args={ "client_encoding": "utf8", # 核心配置:解码遇到非法字节时自动替换,不抛出异常 "decode_errors": "replace" } ) conn = engine.connect() # 验证编码配置 print("Server Encoding ", conn.execute("SHOW SERVER_ENCODING").fetchone()[0]) print("Client Encoding ", conn.execute("SHOW CLIENT_ENCODING").fetchone()[0]) # 正常读取即可,非法字节不会中断流程 df = pandas.read_sql_table(VIEWNAME, conn, schema=SCHEMA)
- 如果排查后发现库内混存大量中文GBK/GB18030编码内容,把
client_encoding参数值改为gb18030即可,中文识别准确率更高。 - 如果不需要保留非法字节的占位,把
decode_errors值改为ignore,驱动会直接跳过非法字节。
方案2:逐行/逐块读取跳过异常数据
如果不想保留损坏字节,要求只读取完全合法的行数据,不要使用read_sql_table一次性全量读取,改用流式游标拉取数据,捕获解码异常跳过坏行:
import pandas as pd from sqlalchemy import text # chunksize设为1就是逐行读取,遇到坏行仅跳过1行,速度较慢 # 设为1000就是逐块读取,遇到坏块跳过1000行,速度更快 chunksize = 1000 valid_data = [] columns = None with engine.connect().execution_options(stream_results=True) as conn: result = conn.execute(text(f"SELECT * FROM {SCHEMA}.{VIEWNAME}")) columns = result.keys() while True: try: rows = result.fetchmany(chunksize) if not rows: break valid_data.extend(rows) except UnicodeDecodeError: # 遇到解码错误直接跳过当前块/行 continue df = pd.DataFrame(valid_data, columns=columns)
注意事项
- 不要尝试将客户端编码设置为
SQL_ASCII,该配置会让驱动直接返回bytes类型数据,pandas读取时会触发类型错误。 - 你之前配置的
client_encoding=utf8未生效的核心原因是SQL_ASCII服务端不做编码转换,配置本身没有问题,只是原始字节里混了非UTF-8内容,和SQLAlchemy配置逻辑无关。 - 对于查找任意行列包含指定字符串的需求,方案1的容错解码配置不会遗漏合法匹配项,性能远高于逐行跳过的方案。
内容的提问来源于stack exchange,提问作者rantsh
相关产品推荐
相关产品推荐

