DataFrame转parquet上传GCP存储桶报pandas解析错误如何解决
问题排查:S3到GCP Parquet文件迁移报错
pandas.errors.ParserError: Error tokenizing data. C error: Expected 2 fields in line 5, saw 3 核心定位结论
首先纠正之前的判断偏差:报错不是最后一行GCP上传代码触发的,问题出在文件读取解析阶段。Error tokenizing data是pandas CSV文本解析器的专属报错,Parquet是二进制列式存储格式,读取合法Parquet文件不会抛出这类文本字段数不匹配的错误,本质原因是S3上后缀为.parquet的文件和实际格式不匹配。
具体根因
- 文件格式错配:S3路径下的
2022/test.parquet实际是CSV/TSV类分隔符文本文件,只是后缀被错误命名为.parquet。pandas调用Parquet解析器读取时识别到文件不符合Parquet二进制规范,会自动降级走通用文本解析逻辑,按默认分隔符逐行拆分字段,读到第5行时发现该行字段数为3,和前几行解析出的2个字段数不一致,就抛出了对应报错。 - 代码存在冗余和隐藏bug:一是重复定义了两次GCP客户端,第一行
client = connections["My-Connection"].storage_client属于无效代码,会干扰排查逻辑;二是文件下载到内存BytesIO对象后,没有把指针重置到文件起始位置,即使格式正确也可能出现读空内容的问题。 - (低概率)Parquet引擎不兼容:如果验证文件确实是合法Parquet,那是pandas依赖的Parquet解析引擎(pyarrow/fastparquet)版本过旧或损坏,导致解析异常触发 fallback 逻辑。
可行解决方案
场景1:S3文件实际为CSV/TSV文本格式(90%概率匹配当前报错)
将读取逻辑替换为对应文本格式的读取方法,处理完成后再转成合法Parquet上传到GCP,修正后代码如下:
import boto3 import io from google.cloud import storage import pandas as pd buffer = io.BytesIO() s3 = boto3.resource('s3', aws_access_key_id='MyKey', aws_secret_access_key='MySecretKey') # 避免用内置类名object做变量名 s3_object = s3.Object('my_bucket_s3','2022/test.parquet') s3_object.download_fileobj(buffer) # 重置内存指针到文件起始位置,否则会读空 buffer.seek(0) # 按实际文件分隔符修改sep参数,制表符分隔就填sep='\t' # on_bad_lines='warn'会打印异常行信息方便排查脏数据,确认无问题可以改成'skip'直接跳过异常行 df = pd.read_csv(buffer, sep=',', on_bad_lines='warn') # 初始化GCP客户端,删除冗余的重复定义 client = storage.Client() bucket = client.get_bucket('my_bucket_gcp') # 转成标准Parquet格式上传 bucket.blob('TEST/test.parquet').upload_from_string(df.to_parquet(engine='pyarrow'), 'parquet')
快速验证方法:把S3上的文件下载到本地,用文本编辑器打开前10行,如果能看到明文的分隔符文本内容,即可确认是后缀错配问题,按本场景处理即可。
场景2:确认S3文件为合法Parquet格式
按以下步骤修复解析环境:
- 升级重装Parquet解析依赖:执行命令
pip install --upgrade pyarrow fastparquet - 读取时显式指定解析引擎:将读取代码改为
df = pd.read_parquet(buffer, engine='pyarrow') - 校验读取到的DataFrame行数、字段符合预期后,再执行上传逻辑。
内容的提问来源于stack exchange,提问作者Rodrigo Maurin Lopez
相关产品推荐
相关产品推荐

